﻿#region References
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Xml;
using System.Reflection;
using System.Windows.Forms;
using CdcSoftware.Pivotal.Engine;
using CdcSoftware.Pivotal.Engine.Client.ClientTasks.Attributes;
using CdcSoftware.Pivotal.Engine.Client.Services.Actions;
using CdcSoftware.Pivotal.Engine.Client.Services.DataAccess;
using CdcSoftware.Pivotal.Engine.Client.Services.Interfaces;
using CdcSoftware.Pivotal.Engine.Client.Types;
using CdcSoftware.Pivotal.Engine.ServerProxy;
using CdcSoftware.Pivotal.Engine.Types.DataTemplates;
using CdcSoftware.Pivotal.Engine.Types.Database;
using CdcSoftware.Pivotal.Engine.Types.Localization;
using CdcSoftware.Pivotal.Engine.Types.Reports;
using CdcSoftware.Pivotal.Engine.Types.Searches;
using CdcSoftware.Pivotal.Engine.Types.Security;
using CdcSoftware.Pivotal.Engine.Types.ServerTasks;
using CdcSoftware.Pivotal.Engine.Types.StaticLists;
using CdcSoftware.Pivotal.Engine.UI;
using CdcSoftware.Pivotal.Engine.UI.DataTypes;
using CdcSoftware.Pivotal.Engine.UI.DataTypes.FormParams;
using CdcSoftware.Pivotal.Engine.UI.DataTypes.Navigation;
using CdcSoftware.Pivotal.Engine.UI.DataTypes.Searches;
using CdcSoftware.Pivotal.Engine.UI.Forms;
using CdcSoftware.Pivotal.Applications.Core.Common;
using CdcSoftware.Pivotal.Engine.Types;
using System.Security.Cryptography;
using System.Windows.Media;
using System.IO;
using Microsoft.PointOfService;
using Pcm.Retail.Client.AppleCare;

#endregion

namespace Pcm.Retail.Client
{
    public partial class Globals : CdcSoftware.Pivotal.Applications.Core.Client.Globals
    {
        #region Variables

        public const string SECURITYKEY = "B3749918-56A1-40A6-A7CC-86C9B56B8374";
        //This Global variable is used when "Cancel" is clicked on "RE_Tender" Form
        //On Cancel SFA_Order record is getting deleted,so user should not be able to see "RE_Tender" form as well as "Sale-Walk-in" form for that SFA_Order record
        private static bool m_salesRecordDeleted = false;
        public static string tempFile;
        private static string[] returnString;
        public static bool PrintError = false;
        public static bool CashDrawerError = false;
        public static object[] AddInstallBase;
        public static DateTime AppleCareSessionDateTime = DateTime.MinValue;
        public static string m_appleCareUserSessionId = string.Empty;
        public static string m_appleCareUserOperationId = string.Empty;

        // Added by thangnv for Serial Number Validation
        private static DataTable m_SaleSerialNumber;
        private static object m_DataGridView;
        private static System.ComponentModel.CancelEventArgs m_Args;
        public static int mRowIdx = -1;

        #endregion

        #region Properties

        /// <summary>
        /// Used to get/set the value of Global Varaible m_salesRecordDeleted
        /// This Global variable is used when "Cancel" is clicked on "RE_Tender" Form
        /// On Cancel SFA_Order record is getting deleted,so user should not be able to see "RE_Tender" form as well as "Sale-Walk-in" form for that SFA_Order record
        /// </summary>
        /// <history>
        /// #Revision   Date            Author  Description
        /// 6.0.9.1     15 Mar 2012     NN      Initial Version
        /// </history>
        public static bool SalesRecordDeleted
        {
            get
            {
                return m_salesRecordDeleted;
            }
            set
            {
                m_salesRecordDeleted = value;
            }
        }

        /// <summary>
        /// This List holds the Promotions selected.
        /// </summary>
        public static List<Object> SelectedPromotions { get; set; }

        /// <summary>
        /// Pane for the StatucStrip.
        /// </summary>
        public enum StatusStripPane
        {
            UserPane,
            EnvironmentPane
        }

        //public static string AppleCareUserSessionId
        public static string AppleCareUserSessionId
        {
            get
            {
                AppleCareAuthenticate();
                return m_appleCareUserSessionId;
            }
        }

        public static string AppleCareOperationId
        {
            get
            {
                AppleCareAuthenticate();
                return m_appleCareUserOperationId;
            }
        }

        public static DataTable dtSaleSerialNumber
        {
            get { return m_SaleSerialNumber; }
            set { m_SaleSerialNumber = value; }
        }

        public static object DataGridView
        {
            get { return m_DataGridView; }
            set { m_DataGridView = value; }
        }

        public static System.ComponentModel.CancelEventArgs Args
        {
            get { return m_Args; }
            set { m_Args = value; }
        }

        #endregion

        #region Public Functions

        /// <summary>
        /// Test if the current user is in a particular security group. 
        /// </summary>
        /// <param name="securityGroupName"></param>
        /// <returns>True or false</returns>
        public static bool CurrentUserInSecurityGroup(string securityGroupName)
        {
            try
            {
                IDataAccessService dataAccess = ClientContext.ClientAPIService.GetService<IDataAccessService>();
                SecurityGroupCollection securityGroups = dataAccess.SystemClient.UserProfile.GetMetaCollection<SecurityGroupCollection>();

                foreach (CdcSoftware.Pivotal.Engine.Types.Security.SecurityGroup securityGroup in securityGroups)
                {
                    if (securityGroup.Name == securityGroupName)
                    {
                        return true;
                    }
                }
                return false;
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Enable (enableFields = True) / disable (enableFields = False) all fields on ALL Primary and Secondary
        /// Segments on the Client Form.
        /// </summary>
        /// <param name="enableFields"></param>
        public static void EnableDisableAllFields(bool enableFields)
        {
            try
            {
                // ### Enable/Disable all fields in Primary Segment
                EnableDisableAllPrimaryFields(enableFields);

                // ### Enable/Disable all fields in Secondary Segments
                EnableDisableAllSecondaries(enableFields);
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Enable (enableFields = True) / disable (enableFields = False) all fields on ALL Primary and Secondary
        /// Segments on the Client Form, adding/removing the "Add Row to Secondary" and "Delete Row from Secondary" 
        /// features from ALL Secondary Segments as specified by the blnAllowUserToAddRows and 
        /// blnAllowUserToDeleteRows variables.
        /// </summary>
        /// <param name="enableFields"></param>
        /// <param name="blnAllowUserToAddRows"></param>
        /// <param name="blnAllowUserToDeleteRows"></param>
        public static void EnableDisableAllFields(bool enableFields, bool blnAllowUserToAddRows, bool blnAllowUserToDeleteRows)
        {
            try
            {
                // ### Enable/Disable all fields in Primary Segment
                EnableDisableAllPrimaryFields(enableFields);

                // ### Enable/Disable all fields in Secondary Segments
                EnableDisableAllSecondaries(enableFields, blnAllowUserToAddRows, blnAllowUserToDeleteRows);
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Enable/Disable ALL fields on the Primary Segment on the current Client Form
        /// </summary>
        /// <param name="enableFields"></param>
        public static void EnableDisableAllPrimaryFields(bool enableFields)
        {
            try
            {
                IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                // ### Enable/Disable all fields in Primary Segment
                // For each Column/Field in the Primary Segment on the Form
                for (int i = 0; i < formView.FormData.DataTemplate.PrimaryDataSection.DataSectionColumns.Count; i++)
                {
                    try
                    {
                        // Enable/Disable the field on the Primary Segment
                        // If the Field is not on the Primary Segment/Data Template for some odd reason, then the
                        // exception will be caught & we'll continue with the rest of the for() loop.
                        formControl.GetControlByDataName(formView.FormData.DataTemplate.PrimaryDataSection.DataSectionColumns[i].Name).Enabled = enableFields;
                    }
                    catch (System.NullReferenceException)
                    {
                        // If the System.NullReferenceException exception has been raised, it means that the 
                        // Field does not actually exist on the Form.  Therefore, ignore it, and continue the 
                        // foreach() loop
                    }
                    catch (Exception exc)
                    {
                        // throw any other type of exception
                        throw new PivotalApplicationCoreException(exc.Message, exc);
                    }
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="enableSecondaries"></param>
        public static void EnableDisableAllSecondaries(bool enableSecondaries)
        {
            try
            {
                IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                // ### Enable/Disable all fields in Secondary Segments
                // For each Secondary Segment on the form...
                for (int i = 0; i < formView.FormData.DataTemplate.SecondaryDataSections.Count; i++)
                {
                    EnableDisableSecondary(enableSecondaries, formView.FormData.DataTemplate.SecondaryDataSections[i].Name);
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Enables/Disables ALL fields on ALL Secondary Segments on the current Client Form, adding/removing the "Add Row to Secondary"
        /// and "Delete Row from Secondary" features as specified by the blnAllowUserToAddRows and blnAllowUserToDeleteRows variables.
        /// </summary>
        /// <param name="enableSecondaries">True: Enable all fields on all Secondaries; False: opposite.</param>
        /// <param name="blnAllowUserToAddRows">True: Display the "+" button on all Secondaries; False: opposite.</param>
        /// <param name="blnAllowUserToDeleteRows">True: Display the "Delete Row" feature on all Secondaries; False: opposite.</param>
        public static void EnableDisableAllSecondaries(bool enableSecondaries, bool blnAllowUserToAddRows, bool blnAllowUserToDeleteRows)
        {
            try
            {
                IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                // ### Enable/Disable all fields in Secondary Segments
                // For each Secondary Segment on the form...
                for (int i = 0; i < formView.FormData.DataTemplate.SecondaryDataSections.Count; i++)
                {
                    EnableDisableSecondary(enableSecondaries, formView.FormData.DataTemplate.SecondaryDataSections[i].Name, blnAllowUserToAddRows, blnAllowUserToDeleteRows);
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Enables or disables a supplied string array of Primary Segment Field Names
        /// on the current Client Form.
        /// </summary>
        /// <param name="enableField">Set to true to enable the list of supplied fields; false to disable</param>
        /// <param name="fieldDataNames">A list of Primary Fields, named by their Data Name - i.e. Table Field Name</param>
        public static void EnableDisablePrimaryFields(bool enableField, string[] fieldDataNames)
        {
            try
            {
                IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                foreach (string fn in fieldDataNames)
                {
                    try
                    {
                        formControl.GetControlByDataName(fn).Enabled = enableField;
                    }
                    catch (System.NullReferenceException)
                    {
                        // If the System.NullReferenceException exception has been raised, it means that the 
                        // Field does not actually exist on the Form.  Therefore, ignore it, and continue the 
                        // foreach() loop
                    }
                    catch (Exception exc)
                    {
                        // throw any other type of exception
                        throw new PivotalApplicationCoreException(exc.Message, exc);
                    }
                }
            }
            catch (Exception exc)
            {
                Globals.HandleException(exc, true);
            }
        }

        /// <summary>
        /// Enables or disables a single Primary field Field on the current Client Form        
        /// </summary>
        /// <param name="enableField">Set to true to enable the list of supplied fields; false to disable</param>
        /// <param name="fieldDataName">A list of Primary Fields, named by their Data Name - i.e. Table Field Name</param>                  
        public static void EnableDisablePrimaryFields(bool enableField, string fieldDataName)
        {
            EnableDisablePrimaryFields(enableField, new string[] { fieldDataName });
        }

        /// <summary>
        /// Sets the ReadOnly and Disabled properties on a supplied string array of Primary
        /// Segment Foreign Key Field Names.  This function is useful to make the dropdown field
        /// itself read-only, but to leave the icon next to the dropdown field enabled (allowing the 
        /// underlying record to be displayed).
        /// </summary>
        /// <param name="setFieldReadOnly">Set to True to disable the dropdown field itself.  Set to False to enable the dropdown field portion.</param>
        /// <param name="setFieldEnabled">Set to True to enable the Icon portion of the field.  Set to False to disable BOTH the dropdown field AND the Icon portions.</param>
        /// <param name="setFieldSpyglassVisible">Set to True to show the Spyglass; False to hide the Spyglass.</param>
        /// <param name="fieldDataNames">String array of Foreign Key Field Names</param>
        public static void EnableDisablePrimaryForeignKeyFields(bool setFieldReadOnly,
                                                                bool setFieldEnabled,
                                                                bool setFieldSpyglassVisible,
                                                                string[] fieldDataNames)
        {
            try
            {
                // Get the Form View and Form Control objects
                IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                // For each field name provided in the list of field names...
                foreach (string fn in fieldDataNames)
                {
                    try
                    {
                        // Only proceed if a non-empty field name was provided
                        if (!(String.IsNullOrEmpty(fn)))
                        {
                            // Assume that the field name we've been given is a valid Foreign Key Field Name, 
                            // and get the Foreign Key Control in question.
                            PivotalForeignKey foreignKeyControl = (PivotalForeignKey)formControl.GetControlByDataName(fn);

                            // If we got a Foreign Key Control back without error, then the foreignKeyControl
                            // object should be not null...
                            if (foreignKeyControl != null)
                            {
                                // ...therefore, set the ReadOnly and Enabled properties accordingly.
                                foreignKeyControl.ReadOnly = setFieldReadOnly;
                                foreignKeyControl.Enabled = setFieldEnabled;
                                foreignKeyControl.SpyglassVisible = setFieldSpyglassVisible;
                            }
                        }
                    }
                    catch (System.NullReferenceException)
                    {
                        // If the System.NullReferenceException exception has been raised, it means that the 
                        // Field does not actually exist on the Form.  Therefore, ignore it, and continue the 
                        // foreach() loop
                    }
                    catch (Exception exc)
                    {
                        // throw any other type of exception
                        throw new PivotalApplicationCoreException(exc.Message, exc);
                    }
                }
            }
            catch (Exception exc)
            {
                Globals.HandleException(exc, true);
            }
        }

        /// <summary>
        /// Sets the ReadOnly and Disabled properties on the supplied Primary
        /// Segment Foreign Key Field Name.  This function is useful to make the dropdown field
        /// itself read-only, but to leave the icon next to the dropdown field enabled (allowing the 
        /// underlying record to be displayed).
        /// </summary>
        /// <param name="setFieldReadOnly">Set to True to disable the dropdown field itself.  Set to False to enable the dropdown field portion.</param>
        /// <param name="setFieldEnabled">Set to True to enable the Icon portion of the field.  Set to False to disable BOTH the dropdown field AND the Icon portions.</param>
        /// <param name="setFieldSpyglassVisible">Set to True to show the Spyglass; False to hide the Spyglass.</param>
        /// <param name="fieldDataName">Foreign Key Field Names</param>             
        public static void EnableDisablePrimaryForeignKeyFields(bool setFieldReadOnly,
                                                                bool setFieldEnabled,
                                                                bool setFieldSpyglassVisible,
                                                                string fieldDataName)
        {
            EnableDisablePrimaryForeignKeyFields(setFieldReadOnly,
                                                 setFieldEnabled,
                                                 setFieldSpyglassVisible,
                                                 new string[] { fieldDataName });
        }

        /// <summary>
        /// Completely Enables or Disables a Secondary with the supplied 'Data Segment' name property value.
        /// If no such Secondary Segment exists, then an exception is NOT thrown.
        /// </summary>
        /// <param name="enableSecondary"></param>
        /// <param name="secondaryDataSegmentName"></param>
        public static void EnableDisableSecondary(bool enableSecondary, string secondaryDataSegmentName)
        {
            try
            {
                if (!(string.IsNullOrEmpty(secondaryDataSegmentName)))
                {
                    IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                    PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                    // Get the Secondary Segment into a PivotalSecondary object...
                    PivotalSecondary secondary = formControl.GetSecondary(secondaryDataSegmentName);

                    // Only continue if we were able to get a valid secondary - no secondary will return null.                    
                    if (secondary != null)
                    {
                        // For each column in that Secondary...
                        for (int j = 0; j < secondary.Columns.Count; j++)
                        {
                            try
                            {
                                // ...either enable it (ReadOnly = False) or disable it (ReadOnly = true)
                                secondary.Columns[j].ReadOnly = !(enableSecondary);
                            }
                            catch (System.NullReferenceException)
                            {
                                // If the System.NullReferenceException exception has been raised, it means that the 
                                // Field does not actually exist on the Secondary.  Therefore, ignore it, and continue the 
                                // foreach() loop
                            }
                            catch (Exception exc)
                            {
                                // throw any other type of exception
                                throw new PivotalApplicationCoreException(exc.Message, exc);
                            }
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Completely Enables or Disables a Secondary with the supplied 'Data Segment' name property value.
        /// If no such Secondary Segment exists, then an exception is NOT thrown.  Adding/removing the "Add Row to Secondary"
        /// and "Delete Row from Secondary" features is controlled by specifying the blnAllowUserToAddRows and 
        /// blnAllowUserToDeleteRows variables.
        /// </summary>
        /// <param name="enableSecondary"></param>
        /// <param name="secondaryDataSegmentName"></param>
        /// <param name="blnAllowUserToAddRows"></param>
        /// <param name="blnAllowUserToDeleteRows"></param>
        public static void EnableDisableSecondary(bool enableSecondary, string secondaryDataSegmentName, bool blnAllowUserToAddRows, bool blnAllowUserToDeleteRows)
        {
            try
            {
                if (!(string.IsNullOrEmpty(secondaryDataSegmentName)))
                {
                    IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                    PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                    // Get the Secondary Segment into a PivotalSecondary object...
                    PivotalSecondary secondary = formControl.GetSecondary(secondaryDataSegmentName);

                    // Only continue if we were able to get a valid secondary - no secondary will return null.                    
                    if (secondary != null)
                    {
                        // For each column in that Secondary...
                        for (int j = 0; j < secondary.Columns.Count; j++)
                        {
                            try
                            {
                                // ...either enable it (ReadOnly = False) or disable it (ReadOnly = true)
                                secondary.Columns[j].ReadOnly = !(enableSecondary);
                            }
                            catch (System.NullReferenceException)
                            {
                                // If the System.NullReferenceException exception has been raised, it means that the 
                                // Field does not actually exist on the Secondary.  Therefore, ignore it, and continue the 
                                // foreach() loop
                            }
                            catch (Exception exc)
                            {
                                // throw any other type of exception
                                throw new PivotalApplicationCoreException(exc.Message, exc);
                            }
                        }

                        // Set properties to determine if the User is allowed to add/delete rows to/from the Secondary
                        // as per the boolean values supplied by the calling method.
                        secondary.AllowUserToAddRows = blnAllowUserToAddRows;
                        secondary.AllowUserToDeleteRows = blnAllowUserToDeleteRows;
                    }
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Enables or Disables the a Column in the provided Secondary Segment Name.  If a supplied column name
        /// does not exist in the secondary segment, then NO exception is thrown.
        /// </summary>
        /// <param name="enableField">Supply 'true' to enable fields; 'false' to disable fields.</param>
        /// <param name="secondaryDataSegmentName">'DataSegment' value of the Secondary in which you want to enable/disable fields</param>
        /// <param name="secondaryTableFieldName">Actual Table Field name of the secondary column you want to enable/disable</param>
        public static void EnableDisableSecondaryFields(bool enableField, string secondaryDataSegmentName, string secondaryTableFieldName)
        {
            try
            {
                EnableDisableSecondaryFields(enableField, secondaryDataSegmentName, new string[] { secondaryTableFieldName });
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Enables or Disables the list of Column Names in the provided Secondary Segment Name.  If a supplied column name
        /// does not exist in the secondary segment, then NO exception is thrown.
        /// </summary>
        /// <param name="enableFields">Supply 'true' to enable fields; 'false' to disable fields.</param>
        /// <param name="secondaryDataSegmentName">'DataSegment' value of the Secondary in which you want to enable/disable fields</param>
        /// <param name="secondaryTableFieldNames">Actual Table Field names of the secondary columns you want to enable/disable</param>
        public static void EnableDisableSecondaryFields(bool enableFields, string secondaryDataSegmentName, string[] secondaryTableFieldNames)
        {
            try
            {
                if (!(string.IsNullOrEmpty(secondaryDataSegmentName)))
                {
                    IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                    PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                    // Get the Secondary Segment into a PivotalSecondary object...
                    PivotalSecondary secondary = formControl.GetSecondary(secondaryDataSegmentName);

                    // Only continue if we were able to get a valid secondary - no secondary will return null, and
                    // if null, there is no point us trying to enable/disable the supplied fields, as this would just take extra time.
                    if (secondary != null)
                    {
                        foreach (string fn in secondaryTableFieldNames)
                        {
                            try
                            {
                                // ...either enable it (ReadOnly = False) or disable it (ReadOnly = true)
                                formControl.GetGridColumn(secondaryDataSegmentName, fn).ReadOnly = !(enableFields);
                            }
                            catch (System.NullReferenceException)
                            {
                                // If the System.NullReferenceException exception has been raised, it means that the 
                                // Field does not actually exist on the Secondary.  Therefore, ignore it, and continue the 
                                // foreach() loop
                            }
                            catch (Exception exc)
                            {
                                // throw any other type of exception
                                throw new PivotalApplicationCoreException(exc.Message, exc);
                            }
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Enables or Disables an entire row in the specified Secondary Segment which has a Data Segment value matching the supplied 
        /// secondaryDataSegmentName.  rowIndex is the 0-based row to enable/disable.  If the row does not exist in the secondary, 
        /// an exception will be thrown.
        /// </summary>
        /// <param name="enableFields">Supply 'true' to enable fields; 'false' to disable fields.</param>
        /// <param name="secondaryDataSegmentName">'DataSegment' value of the Secondary in which you want to enable/disable fields</param>
        /// <param name="rowIndex">0-based row number in the Secondary Segment for which all columns should be enabled/disabled.</param>
        public static void EnableDisableSecondaryRow(bool enableFields, string secondaryDataSegmentName, int rowIndex)
        {
            try
            {
                if (!(string.IsNullOrEmpty(secondaryDataSegmentName)))
                {
                    IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                    PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                    // Get the Secondary Segment into a PivotalSecondary object...
                    PivotalSecondary secondary = formControl.GetSecondary(secondaryDataSegmentName);

                    // Only continue if we were able to get a valid secondary - no secondary will return null, and
                    // if null, there is no point us trying to enable/disable the supplied fields, as this would just take extra time.
                    if (secondary != null)
                    {
                        // Secondary is valid.  Now go through each column in the secondary, and for the specific row
                        // in question, enable or disable each column.
                        for (int j = 0; j < secondary.Columns.Count; j++)
                        {
                            // enable/disable column/cell 'j' for row number 'rowIndex'.  If 'rowIndex' is not a valid row,
                            // then an exception will be thrown.
                            secondary.Rows[rowIndex].Cells[j].ReadOnly = !(enableFields);
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Enables or Disables an entire row in the specified Secondary Segment which has a Data Segment value matching the supplied 
        /// secondaryDataSegmentName.  rowIndex is the 0-based row to enable/disable.  If the row does not exist in the secondary, 
        /// an exception will be thrown.
        /// </summary>
        /// <param name="enableFields">Supply 'true' to enable fields; 'false' to disable fields.</param>
        /// <param name="secondaryDataSegmentName">'DataSegment' value of the Secondary in which you want to enable/disable fields</param>
        /// <param name="rowIndex">0-based row number in the Secondary Segment for which all columns should be enabled/disabled.</param>
        /// <param name="secondaryTableFieldNames">Actual Table Field names of the secondary columns you want to enable/disable</param>
        public static void EnableDisableSecondaryRow(bool enableFields, string secondaryDataSegmentName, int rowIndex, string[] secondaryTableFieldNames)
        {
            try
            {
                if (!(string.IsNullOrEmpty(secondaryDataSegmentName)))
                {
                    IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                    PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                    // Get the Secondary Segment into a PivotalSecondary object...
                    PivotalSecondary secondary = formControl.GetSecondary(secondaryDataSegmentName);

                    // Only continue if we were able to get a valid secondary - no secondary will return null, and
                    // if null, there is no point us trying to enable/disable the supplied fields, as this would just take extra time.
                    if (secondary != null)
                    {
                        foreach (string fn in secondaryTableFieldNames)
                        {
                            string dataGridViewColumnName = String.Empty;

                            try
                            {
                                // secondaryTableFieldNames contains the names of the underying Table Fields in the Secondary Segment -
                                // i.e. the Secondary Segment's DataPropertyName property for each column in the Secondary Segment.
                                // We must now get the "Name" property of the same column in the Secondary Segment.
                                dataGridViewColumnName = formControl.GetGridColumn(secondaryDataSegmentName, fn).Name;
                            }
                            catch (System.NullReferenceException)
                            {
                                // If the System.NullReferenceException exception has been raised, it means that the 
                                // Field does not actually exist on the Secondary.  Therefore, ignore it, and continue the 
                                // foreach() loop
                            }
                            catch (Exception exc)
                            {
                                // throw any other type of exception
                                throw new PivotalApplicationCoreException(exc.Message, exc);
                            }

                            // if a valid Table Field name on this Secondary Segment was supplied, we should now have the 
                            // Name property in dataGridViewColumnName.
                            // if not, an exception will have been thrown, which we will NOT throw upwards - i.e. we will
                            // assume that the calling code has given us a rubbish Table Field Name
                            if (!(string.IsNullOrEmpty(dataGridViewColumnName)))
                            {
                                // enable/disable column/cell 'dataGridViewColumnName' for row number 'rowIndex'.  
                                // If 'rowIndex' is not a valid row, then an exception will be thrown.
                                secondary.Rows[rowIndex].Cells[dataGridViewColumnName].ReadOnly = !(enableFields);
                            }
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Encrypts the string value and returns the encrypted value.
        /// </summary>
        /// <param name="toEncrypt">Send the string value for encryption</param>
        /// <returns>Encrypted cipher string.</returns>
        public static string Encrypt(string toEncrypt)
        {
            try
            {
                byte[] keyArray;
                byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(Globals.SECURITYKEY));
                hashmd5.Clear();
                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
                tdes.Key = keyArray;
                tdes.Mode = CipherMode.ECB;
                tdes.Padding = PaddingMode.PKCS7;
                ICryptoTransform cTransform = tdes.CreateEncryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                tdes.Clear();

                return Convert.ToBase64String(resultArray, 0, resultArray.Length);
            }
            catch (Exception e)
            {
                throw new PivotalApplicationCoreException(e.Message, e);
            }
        }

        /// <summary>
        /// Decrypts the cipher string value and returns the decrypted value.
        /// </summary>
        /// <param name="cipherString">Send the cipher string value for decryption</param>
        /// <returns>Decrypted string.</returns>
        public static string Decrypt(string cipherString)
        {
            try
            {
                byte[] keyArray;
                byte[] toEncryptArray = Convert.FromBase64String(cipherString);

                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(Globals.SECURITYKEY));
                hashmd5.Clear();
                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
                tdes.Key = keyArray;
                tdes.Mode = CipherMode.ECB;
                tdes.Padding = PaddingMode.PKCS7;
                ICryptoTransform cTransform = tdes.CreateDecryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                tdes.Clear();

                return UTF8Encoding.UTF8.GetString(resultArray);
            }
            catch (Exception e)
            {
                throw new PivotalApplicationCoreException(e.Message, e);
            }
        }

        /// <summary>
        /// Returns the text value of a foreign field on the current form primary section for the given
        /// column by implement the following logic:
        /// - Gets the current form by calling ClientContext.ClientAPIService.GetActiveContentObject(),
        /// - Gets the DataSet by calling IFormView.FormData.DataSet,
        /// - Gets the primary DataSection object by calling FormData.DataTemplate.PrimaryDataSection,
        /// - Gets the Column object by calling DataSection.Table.Columns[string],
        /// - If the column Is ForeignKey then gets the display column name by calling Column.DisplayName and returns
        ///   the foreign key display value by calling TypeConvert.ToString(DataSet.Tables[string].Rows[0][string]).
        /// - Otherwise return empty string.
        /// </summary>
        /// <remarks>Note: this function is only used to look for the text value in primary record.</remarks>
        /// <param name="columnName">The field name</param>
        /// <returns>The text value of the specified foreign Column</returns>
        public new static string GetForeignColumnText(string columnName)
        {
            IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
            if (formView != null)
            {
                DataSet dataSet = formView.FormData.DataSet;
                DataSection primaryDataSection = formView.FormData.DataTemplate.PrimaryDataSection;
                Column column = primaryDataSection.Table.Columns[columnName];

                if (column.IsForeignKey)
                {
                    // The OTB Foundation Library Globals Client Task version of this method returns
                    // column.DisplayName.  This is wrong, it needs to return column.DescriptorColumnName in order to return
                    // the text value displayed in the FK Field.  Therefore, return the appropriate property below...
                    return TypeConvert.ToString(dataSet.Tables[primaryDataSection.Name].Rows[0][column.DescriptorColumnName]);
                }
            }

            return string.Empty;
        }

        /// <summary>
        /// Safely remove the Buttons named within the supplied buttonNames string array from the collection of Button Bar
        /// Buttons along the bottom of the Client Form.  If the Button does not exist in this collection, 
        /// then we don't even try to remove it, as if you try to remove a Button which does not exist, an
        /// exception is thrown, which is obviously bad.  This code safeguards against that eventuality and does
        /// things 'nicely'.
        /// </summary>
        /// <param name="buttonNames"></param>
        public static void RemoveButtonBarButton(string[] buttonNames)
        {
            try
            {
                foreach (string buttonName in buttonNames)
                {
                    // Only continue if a non-null/"" Button Name is supplied
                    if (!(string.IsNullOrEmpty(buttonName)))
                    {
                        // Get the Form View object for the current Client Form
                        IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();

                        // By default, assume that the supplied Button Name is NOT in the collection of Button Bar Buttons
                        bool blnButtonNameExistsInButtonCollection = false;

                        // Iterate through the collection of Button Bar Buttons...
                        foreach (ButtonBarButton b in formView.ButtonBar.Buttons)
                        {
                            // Is the name of the Button we're looking for = to the current Button being looked at in the collection?
                            if (b.Name == buttonName)
                            {
                                // Yes - the Button we're looking for IS in the set of Button Bar Buttons
                                // Set blnButtonNameExistsInButtonCollection = true, and exit the foreach loop
                                blnButtonNameExistsInButtonCollection = true;
                                break;
                            }
                        }

                        // If blnButtonNameExistsInButtonCollection = true, then this means that the button we're looking
                        // for DOES exist in the collection of Button Bar Buttons...
                        if (blnButtonNameExistsInButtonCollection)
                        {
                            // ...so remove it!  This ensures no Exception is thrown if we try to remove a button which
                            // does not exist in the collection of Button Bar Buttons.
                            formView.ButtonBar.Buttons.Remove(formView.ButtonBar.Buttons[buttonName]);
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Safely remove a Button with the supplied strButtonName from the collection of Button Bar
        /// Buttons along the bottom of the Client Form.  If the Button does not exist in this collection, 
        /// then we don't even try to remove it, as if you try to remove a Button which does not exist, an
        /// exception is thrown, which is obviously bad.  This code safeguards against that eventuality and does
        /// things 'nicely'.
        /// </summary>
        /// <param name="strButtonName">Name of the Button Bar Button to remove</param>
        public static void RemoveButtonBarButton(string strButtonName)
        {
            try
            {
                RemoveButtonBarButton(new string[] { strButtonName });
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Executes the specified Query, and returns a DataTable containing the selected rows/records from the Query Results.  
        /// The field name specified in the strFieldNameToReturn variable must be the Primary Key Record Id field of the Query's Table (the Table Name is also specified
        /// in the strTableName variable).  So, if for example, you are executing a Query against the Account Table, then strFieldNameToReturn must be "Account_Id".
        /// </summary>
        /// <param name="strQueryName">Name of Query to execute</param>
        /// <param name="strTableName">Name of the Table which the Query belongs to</param>
        /// <param name="strSearchTitle">The text to put in the Title section of the Query window.  Supply empty string for no text.</param>
        /// <param name="strSearchInstructionalText">Helper text, indicating what the User should do.  Supply empty string for no text.</param>
        /// <param name="strFieldNameToReturn">Primary Key Field Name of the Query's Table</param>
        /// <returns></returns>
        public static DataTable SelectMultipleRecordsFromQuery(string strQueryName, string strTableName, string strSearchTitle, string strSearchInstructionalText, string strFieldNameToReturn)
        {
            try
            {
                // The selected record(s) will be returned in this DataTable variable
                DataTable dtReturn = null;

                // Get the Search Service, so we can execute Searches/Queries
                ISearchService searchService = ClientContext.ClientAPIService.GetService<ISearchService>();

                // Important: We need this searchParameters object, even if we don't do anything with it (other than supply it to the ShowMultiSelectModal call), as if
                // we don't supply it to ShowMultiSelectModal, then the "Run Search" button will disappear after the first search is executed.
                SearchParamsCollection searchParameters = new SearchParamsCollection();

                // Execute the supplied Query
                IMultiSelectResult multiSelectResult = searchService.ShowMultiSelectModal(strQueryName, strTableName,
                                                                                          searchParameters, null, false,
                                                                                          strSearchTitle, strSearchInstructionalText,
                                                                                          null, null);

                // Only continue if something was returned after clicking OK/Cancel/Close
                if (multiSelectResult != null)
                {
                    // If the User pressed the "OK" button...
                    if (multiSelectResult.SelectedButtonIdentifier == "1")
                    {
                        if (multiSelectResult.SelectedRecords != null)
                        {
                            if (multiSelectResult.SelectedRecords.Rows.Count >= 1)
                            {
                                // if >= 1 record was returned, then return them to the calling function.
                                dtReturn = multiSelectResult.SelectedRecords;
                            }
                        }
                    }
                }

                // Return the DataTable containing the selected records.
                return dtReturn;
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Executes the specified Query, and returns a DataTable containing the selected rows/records from the Query Results.  
        /// The field name specified in the strFieldNameToReturn variable must be the Primary Key Record Id field of the Query's Table (the Table Name is also specified
        /// in the strTableName variable).  So, if for example, you are executing a Query against the Account Table, then strFieldNameToReturn must be "Account_Id".
        /// This overload allows you to supply Parameter Names and Values to the Query being executed.  The parameterNames[] and parameterValues[]
        /// arrays must have the same numbers of elements.
        /// </summary>
        /// <param name="strQueryName">Name of Query to execute</param>
        /// <param name="strTableName">Name of the Table which the Query belongs to</param>
        /// <param name="strSearchTitle">The text to put in the Title section of the Query window.  Supply empty string for no text.</param>
        /// <param name="strSearchInstructionalText">Helper text, indicating what the User should do.  Supply empty string for no text.</param>
        /// <param name="strFieldNameToReturn">Primary Key Field Name of the Query's Table</param>
        /// <param name="parameterNames">String array of the Names of the Parameters to pass to the Query prior to its execution.  Must have the same number of elements as parameterValues.</param>
        /// <param name="parameterValues">Object array of the Values of the Parameters to pass to the Query prior to its execution.  Must have the same number of elements as parameterNames.</param>
        /// <returns></returns>
        public static DataTable SelectMultipleRecordsFromQuery(string strQueryName, string strTableName, string strSearchTitle, string strSearchInstructionalText, string strFieldNameToReturn,
                                                               string[] parameterNames, object[] parameterValues)
        {
            try
            {
                // The selected record(s) will be returned in this DataTable variable
                DataTable dtReturn = null;

                // Get the Search Service, so we can execute Searches/Queries
                ISearchService searchService = ClientContext.ClientAPIService.GetService<ISearchService>();

                // Important: We need this searchParameters object, even if we don't do anything with it (other than supply it to the ShowMultiSelectModal call), as if
                // we don't supply it to ShowMultiSelectModal, then the "Run Search" button will disappear after the first search is executed.
                SearchParamsCollection searchParameters = new SearchParamsCollection();

                // At least one of the parameterNames or parameterValues is non-null, so we likely have to pass parameters to the Query
                if ((parameterNames != null) || (parameterValues != null))
                {
                    // parameterNames is null though - both should be non-null.  Throw error.
                    if (parameterNames == null)
                    {
                        throw new PivotalApplicationCoreException("A non-null parameterValues array was supplied, but the parameterNames array is null.  Both arrays must either be null, or non-null and contain the same number of elements.");
                    }

                    // parameterValues is null though - both should be non-null.  Throw error.
                    if (parameterValues == null)
                    {
                        throw new PivotalApplicationCoreException("A non-null parameterNames array was supplied, but the parameterValues array is null.  Both arrays must either be null, or non-null and contain the same number of elements.");
                    }

                    // Both parameter arrays are non-null but do not have the same length.  Throw error.
                    if (parameterNames.Length != parameterValues.Length)
                    {
                        throw new PivotalApplicationCoreException("The parameterNames and parameterValues arrays do not contain the same numbers of elements.  Unable to execute Query.");
                    }

                    // Iterate through Parameter Name and Value arrays, adding each pair as a Parameter to the Search Parameters collection
                    if (parameterNames.Length > 0)
                    {
                        for (int i = 0; i < parameterNames.Length; i++)
                        {
                            SearchParam sp = new SearchParam();
                            sp.ParamName = parameterNames[i];
                            sp.ParamValue = parameterValues[i];
                            searchParameters.Add(sp);
                        }
                    }
                }

                // Execute the supplied Query
                IMultiSelectResult multiSelectResult = searchService.ShowMultiSelectModal(strQueryName, strTableName,
                                                                                          searchParameters, null, false,
                                                                                          strSearchTitle, strSearchInstructionalText,
                                                                                          null, null);

                // Only continue if something was returned after clicking OK/Cancel/Close
                if (multiSelectResult != null)
                {
                    // If the User pressed the "OK" button...
                    if (multiSelectResult.SelectedButtonIdentifier == "1")
                    {
                        if (multiSelectResult.SelectedRecords != null)
                        {
                            if (multiSelectResult.SelectedRecords.Rows.Count >= 1)
                            {
                                // if >= 1 record was returned, then return them to the calling function.
                                dtReturn = multiSelectResult.SelectedRecords;
                            }
                        }
                    }
                }

                // Return the DataTable containing the selected records.
                return dtReturn;
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Executes the specified Query, and returns a DataTable containing the selected rows/records from the Query Results.  
        /// The field name specified in the strFieldNameToReturn variable must be the Primary Key Record Id field of the Query's Table (the Table Name is also specified
        /// in the strTableName variable).  So, if for example, you are executing a Query against the Account Table, then strFieldNameToReturn must be "Account_Id".
        /// This overload allows you to supply Parameter Names and Values to the Query being executed.  The parameterNames[] and parameterValues[]
        /// arrays must have the same numbers of elements.
        /// </summary>
        /// <param name="strQueryName">Name of Query to execute</param>
        /// <param name="strTableName">Name of the Table which the Query belongs to</param>
        /// <param name="strSearchTitle">The text to put in the Title section of the Query window.  Supply empty string for no text.</param>
        /// <param name="strSearchInstructionalText">Helper text, indicating what the User should do.  Supply empty string for no text.</param>
        /// <param name="strFieldNameToReturn">Primary Key Field Name of the Query's Table</param>
        /// <param name="strSearchResultsListName">Name of the Search Results List within which the results should be displayed.</param>
        /// <returns></returns>
        public static DataTable SelectMultipleRecordsFromQuery(string strQueryName, string strTableName, string strSearchTitle, string strSearchInstructionalText, string strFieldNameToReturn,
                                                               string strSearchResultsListName)
        {
            try
            {
                // The selected record(s) will be returned in this DataTable variable
                DataTable dtReturn = null;

                // Get the Search Service, so we can execute Searches/Queries
                ISearchService searchService = ClientContext.ClientAPIService.GetService<ISearchService>();

                // Important: We need this searchParameters object, even if we don't do anything with it (other than supply it to the ShowMultiSelectModal call), as if
                // we don't supply it to ShowMultiSelectModal, then the "Run Search" button will disappear after the first search is executed.
                SearchParamsCollection searchParameters = new SearchParamsCollection();

                // Get the supplied Search Results List name
                SystemSearchResultsList ssrl = SystemClient.UserProfile.GetMetaItem<SystemSearchResultsList>(strSearchResultsListName);
                ISearchResultsListFields srlf = searchService.CreateSearchResultsListFields();
                srlf.SRLId = ssrl.Id;

                // Execute the supplied Query
                IMultiSelectResult multiSelectResult = searchService.ShowMultiSelectModal(strQueryName, strTableName,
                                                                                          searchParameters, null, false,
                                                                                          strSearchTitle, strSearchInstructionalText,
                                                                                          null, srlf);

                // Only continue if something was returned after clicking OK/Cancel/Close
                if (multiSelectResult != null)
                {
                    // If the User pressed the "OK" button...
                    if (multiSelectResult.SelectedButtonIdentifier == "1")
                    {
                        if (multiSelectResult.SelectedRecords != null)
                        {
                            if (multiSelectResult.SelectedRecords.Rows.Count >= 1)
                            {
                                // if >= 1 record was returned, then return them to the calling function.
                                dtReturn = multiSelectResult.SelectedRecords;
                            }
                        }
                    }
                }

                // Return the DataTable containing the selected records.
                return dtReturn;
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Executes the specified Query, and returns a DataTable containing the selected rows/records from the Query Results.  
        /// The field name specified in the strFieldNameToReturn variable must be the Primary Key Record Id field of the Query's Table (the Table Name is also specified
        /// in the strTableName variable).  So, if for example, you are executing a Query against the Account Table, then strFieldNameToReturn must be "Account_Id".
        /// This overload allows you to supply Parameter Names and Values to the Query being executed.  The parameterNames[] and parameterValues[]
        /// arrays must have the same numbers of elements.
        /// </summary>
        /// <param name="strQueryName">Name of Query to execute</param>
        /// <param name="strTableName">Name of the Table which the Query belongs to</param>
        /// <param name="strSearchTitle">The text to put in the Title section of the Query window.  Supply empty string for no text.</param>
        /// <param name="strSearchInstructionalText">Helper text, indicating what the User should do.  Supply empty string for no text.</param>
        /// <param name="strFieldNameToReturn">Primary Key Field Name of the Query's Table</param>
        /// <param name="parameterNames">String array of the Names of the Parameters to pass to the Query prior to its execution.  Must have the same number of elements as parameterValues.</param>
        /// <param name="parameterValues">Object array of the Values of the Parameters to pass to the Query prior to its execution.  Must have the same number of elements as parameterNames.</param>
        /// <param name="strSearchResultsListName">Name of the Search Results List within which the results should be displayed.</param>
        /// <returns></returns>
        public static DataTable SelectMultipleRecordsFromQuery(string strQueryName, string strTableName, string strSearchTitle, string strSearchInstructionalText, string strFieldNameToReturn,
                                                               string[] parameterNames, object[] parameterValues, string strSearchResultsListName)
        {
            try
            {
                // The selected record(s) will be returned in this DataTable variable
                DataTable dtReturn = null;

                // Get the Search Service, so we can execute Searches/Queries
                ISearchService searchService = ClientContext.ClientAPIService.GetService<ISearchService>();

                // Important: We need this searchParameters object, even if we don't do anything with it (other than supply it to the ShowMultiSelectModal call), as if
                // we don't supply it to ShowMultiSelectModal, then the "Run Search" button will disappear after the first search is executed.
                SearchParamsCollection searchParameters = new SearchParamsCollection();

                // At least one of the parameterNames or parameterValues is non-null, so we likely have to pass parameters to the Query
                if ((parameterNames != null) || (parameterValues != null))
                {
                    // parameterNames is null though - both should be non-null.  Throw error.
                    if (parameterNames == null)
                    {
                        throw new PivotalApplicationCoreException("A non-null parameterValues array was supplied, but the parameterNames array is null.  Both arrays must either be null, or non-null and contain the same number of elements.");
                    }

                    // parameterValues is null though - both should be non-null.  Throw error.
                    if (parameterValues == null)
                    {
                        throw new PivotalApplicationCoreException("A non-null parameterNames array was supplied, but the parameterValues array is null.  Both arrays must either be null, or non-null and contain the same number of elements.");
                    }

                    // Both parameter arrays are non-null but do not have the same length.  Throw error.
                    if (parameterNames.Length != parameterValues.Length)
                    {
                        throw new PivotalApplicationCoreException("The parameterNames and parameterValues arrays do not contain the same numbers of elements.  Unable to execute Query.");
                    }

                    // Iterate through Parameter Name and Value arrays, adding each pair as a Parameter to the Search Parameters collection
                    if (parameterNames.Length > 0)
                    {
                        for (int i = 0; i < parameterNames.Length; i++)
                        {
                            SearchParam sp = new SearchParam();
                            sp.ParamName = parameterNames[i];
                            sp.ParamValue = parameterValues[i];
                            searchParameters.Add(sp);
                        }
                    }
                }

                // Get the supplied Search Results List name
                SystemSearchResultsList ssrl = SystemClient.UserProfile.GetMetaItem<SystemSearchResultsList>(strSearchResultsListName);
                ISearchResultsListFields srlf = searchService.CreateSearchResultsListFields();
                srlf.SRLId = ssrl.Id;

                // Execute the supplied Query
                IMultiSelectResult multiSelectResult = searchService.ShowMultiSelectModal(strQueryName, strTableName,
                                                                                          searchParameters, null, false,
                                                                                          strSearchTitle, strSearchInstructionalText,
                                                                                          null, srlf);

                // Only continue if something was returned after clicking OK/Cancel/Close
                if (multiSelectResult != null)
                {
                    // If the User pressed the "OK" button...
                    if (multiSelectResult.SelectedButtonIdentifier == "1")
                    {
                        if (multiSelectResult.SelectedRecords != null)
                        {
                            if (multiSelectResult.SelectedRecords.Rows.Count >= 1)
                            {
                                // if >= 1 record was returned, then return them to the calling function.
                                dtReturn = multiSelectResult.SelectedRecords;
                            }
                        }
                    }
                }

                // Return the DataTable containing the selected records.
                return dtReturn;
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Executes the specified Query, and returns the selected Record Id from the Query Results.  If the User selects multiple rows for some reason, then the first selected record
        /// is returned.  The field name specified in the strFieldNameToReturn variable must be the Primary Key Record Id field of the Query's Table (the Table Name is also specified
        /// in the strTableName variable).  So, if for example, you are executing a Query against the Account Table, then strFieldNameToReturn must be "Account_Id".
        /// </summary>
        /// <param name="strQueryName">Name of Query to execute</param>
        /// <param name="strTableName">Name of the Table which the Query belongs to</param>
        /// <param name="strSearchTitle">The text to put in the Title section of the Query window.  Supply empty string for no text.</param>
        /// <param name="strSearchInstructionalText">Helper text, indicating what the User should do.  Supply empty string for no text.</param>
        /// <param name="strFieldNameToReturn">Primary Key Field Name of the Query's Table</param>
        /// <returns></returns>
        public static Id SelectSingleRecordFromQuery(string strQueryName, string strTableName, string strSearchTitle, string strSearchInstructionalText, string strFieldNameToReturn)
        {
            try
            {
                // The selected Record Id (if any) will be returned in this variable.
                Id vntSelectedRecordId = null;

                // Get the Search Service, so we can execute Searches/Queries
                ISearchService searchService = ClientContext.ClientAPIService.GetService<ISearchService>();

                // Important: We need this searchParameters object, even if we don't do anything with it (other than supply it to the ShowMultiSelectModal call), as if
                // we don't supply it to ShowMultiSelectModal, then the "Run Search" button will disappear after the first search is executed.
                SearchParamsCollection searchParameters = new SearchParamsCollection();

                // Execute the supplied Query
                IMultiSelectResult multiSelectResult = searchService.ShowMultiSelectModal(strQueryName, strTableName,
                                                                                          searchParameters, null, false,
                                                                                          strSearchTitle, strSearchInstructionalText,
                                                                                          null, null);

                // Only continue if something was returned after clicking OK/Cancel/Close
                if (multiSelectResult != null)
                {
                    // If the User pressed the "OK" button...
                    if (multiSelectResult.SelectedButtonIdentifier == "1")
                    {
                        if (multiSelectResult.SelectedRecords != null)
                        {
                            if (multiSelectResult.SelectedRecords.Rows.Count >= 1)
                            {
                                // Even if the User has selected > 1 record for some odd reason (this is meant to be single-select), just grab
                                // the first selected record.
                                vntSelectedRecordId = Id.Create(multiSelectResult.SelectedRecords.Rows[0][strFieldNameToReturn]);

                                if (vntSelectedRecordId != null)
                                {
                                    return vntSelectedRecordId;
                                }
                                else
                                {
                                    // Throw error if null Record Id returned from record - this should never happen
                                    throw new PivotalApplicationCoreException("A record was selected from the results of the '" + strQueryName + "' Query which, for some reason, has a null Record Id.  This should never happen.  Please contact your Administrator.");
                                }
                            }
                        }
                    }
                }

                // Return either the Selected Record Id, or Null (if the User pressed cancel, or selected > 1 record, etc).
                return vntSelectedRecordId;
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Executes the specified Query, and returns the selected Record Id from the Query Results.  If the User selects multiple rows for some reason, then the first selected record
        /// is returned.  The field name specified in the strFieldNameToReturn variable must be the Primary Key Record Id field of the Query's Table (the Table Name is also specified
        /// in the strTableName variable).  So, if for example, you are executing a Query against the Account Table, then strFieldNameToReturn must be "Account_Id".
        /// This overload allows you to supply Parameter Names and Values to the Query being executed.  The parameterNames[] and parameterValues[]
        /// arrays must have the same numbers of elements.
        /// </summary>
        /// <param name="strQueryName">Name of Query to execute</param>
        /// <param name="strTableName">Name of the Table which the Query belongs to</param>
        /// <param name="strSearchTitle">The text to put in the Title section of the Query window.  Supply empty string for no text.</param>
        /// <param name="strSearchInstructionalText">Helper text, indicating what the User should do.  Supply empty string for no text.</param>
        /// <param name="strFieldNameToReturn">Primary Key Field Name of the Query's Table</param>
        /// <param name="parameterNames">String array of the Names of the Parameters to pass to the Query prior to its execution.  Must have the same number of elements as parameterValues.</param>
        /// <param name="parameterValues">Object array of the Values of the Parameters to pass to the Query prior to its execution.  Must have the same number of elements as parameterNames.</param>
        /// <returns></returns>
        public static Id SelectSingleRecordFromQuery(string strQueryName, string strTableName, string strSearchTitle, string strSearchInstructionalText, string strFieldNameToReturn,
                                                     string[] parameterNames, object[] parameterValues)
        {
            try
            {
                // The selected Record Id (if any) will be returned in this variable.
                Id vntSelectedRecordId = null;

                // Get the Search Service, so we can execute Searches/Queries
                ISearchService searchService = ClientContext.ClientAPIService.GetService<ISearchService>();

                // Important: We need this searchParameters object, even if we don't do anything with it (other than supply it to the ShowMultiSelectModal call), as if
                // we don't supply it to ShowMultiSelectModal, then the "Run Search" button will disappear after the first search is executed.
                SearchParamsCollection searchParameters = new SearchParamsCollection();

                // At least one of the parameterNames or parameterValues is non-null, so we likely have to pass parameters to the Query
                if ((parameterNames != null) || (parameterValues != null))
                {
                    // parameterNames is null though - both should be non-null.  Throw error.
                    if (parameterNames == null)
                    {
                        throw new PivotalApplicationCoreException("A non-null parameterValues array was supplied, but the parameterNames array is null.  Both arrays must either be null, or non-null and contain the same number of elements.");
                    }

                    // parameterValues is null though - both should be non-null.  Throw error.
                    if (parameterValues == null)
                    {
                        throw new PivotalApplicationCoreException("A non-null parameterNames array was supplied, but the parameterValues array is null.  Both arrays must either be null, or non-null and contain the same number of elements.");
                    }

                    // Both parameter arrays are non-null but do not have the same length.  Throw error.
                    if (parameterNames.Length != parameterValues.Length)
                    {
                        throw new PivotalApplicationCoreException("The parameterNames and parameterValues arrays do not contain the same numbers of elements.  Unable to execute Query.");
                    }

                    // Iterate through Parameter Name and Value arrays, adding each pair as a Parameter to the Search Parameters collection
                    if (parameterNames.Length > 0)
                    {
                        for (int i = 0; i < parameterNames.Length; i++)
                        {
                            SearchParam sp = new SearchParam();
                            sp.ParamName = parameterNames[i];
                            sp.ParamValue = parameterValues[i];
                            searchParameters.Add(sp);
                        }
                    }
                }

                // Execute the supplied Query
                IMultiSelectResult multiSelectResult = searchService.ShowMultiSelectModal(strQueryName, strTableName,
                                                                                          searchParameters, null, false,
                                                                                          strSearchTitle, strSearchInstructionalText,
                                                                                          null, null);

                // Only continue if something was returned after clicking OK/Cancel/Close
                if (multiSelectResult != null)
                {
                    // If the User pressed the "OK" button...
                    if (multiSelectResult.SelectedButtonIdentifier == "1")
                    {
                        if (multiSelectResult.SelectedRecords != null)
                        {
                            if (multiSelectResult.SelectedRecords.Rows.Count >= 1)
                            {
                                // Even if the User has selected > 1 record for some odd reason (this is meant to be single-select), just grab
                                // the first selected record.
                                vntSelectedRecordId = Id.Create(multiSelectResult.SelectedRecords.Rows[0][strFieldNameToReturn]);

                                if (vntSelectedRecordId != null)
                                {
                                    return vntSelectedRecordId;
                                }
                                else
                                {
                                    // Throw error if null Record Id returned from record - this should never happen
                                    throw new PivotalApplicationCoreException("A record was selected from the results of the '" + strQueryName + "' Query which, for some reason, has a null Record Id.  This should never happen.  Please contact your Administrator.");
                                }
                            }
                        }
                    }
                }

                // Return either the Selected Record Id, or Null (if the User pressed cancel, or selected > 1 record, etc).
                return vntSelectedRecordId;
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Executes the specified Query, and returns the selected Record Id from the Query Results.  If the User selects multiple rows for some reason, then the first selected record
        /// is returned.  The field name specified in the strFieldNameToReturn variable must be the Primary Key Record Id field of the Query's Table (the Table Name is also specified
        /// in the strTableName variable).  So, if for example, you are executing a Query against the Account Table, then strFieldNameToReturn must be "Account_Id".
        /// This overload allows you to supply Parameter Names and Values to the Query being executed.  The parameterNames[] and parameterValues[]
        /// arrays must have the same numbers of elements.
        /// </summary>
        /// <param name="strQueryName">Name of Query to execute</param>
        /// <param name="strTableName">Name of the Table which the Query belongs to</param>
        /// <param name="strSearchTitle">The text to put in the Title section of the Query window.  Supply empty string for no text.</param>
        /// <param name="strSearchInstructionalText">Helper text, indicating what the User should do.  Supply empty string for no text.</param>
        /// <param name="strFieldNameToReturn">Primary Key Field Name of the Query's Table</param>
        /// <param name="strSearchResultsListName">Name of the Search Results List within which the results should be displayed.</param>
        /// <returns></returns>
        public static Id SelectSingleRecordFromQuery(string strQueryName, string strTableName, string strSearchTitle, string strSearchInstructionalText, string strFieldNameToReturn,
                                                     string strSearchResultsListName)
        {
            try
            {
                // The selected Record Id (if any) will be returned in this variable.
                Id vntSelectedRecordId = null;

                // Get the Search Service, so we can execute Searches/Queries
                ISearchService searchService = ClientContext.ClientAPIService.GetService<ISearchService>();

                // Important: We need this searchParameters object, even if we don't do anything with it (other than supply it to the ShowMultiSelectModal call), as if
                // we don't supply it to ShowMultiSelectModal, then the "Run Search" button will disappear after the first search is executed.
                SearchParamsCollection searchParameters = new SearchParamsCollection();

                // Get the supplied Search Results List name
                SystemSearchResultsList ssrl = SystemClient.UserProfile.GetMetaItem<SystemSearchResultsList>(strSearchResultsListName);
                ISearchResultsListFields srlf = searchService.CreateSearchResultsListFields();
                srlf.SRLId = ssrl.Id;

                // Execute the supplied Query
                IMultiSelectResult multiSelectResult = searchService.ShowMultiSelectModal(strQueryName, strTableName,
                                                                                          searchParameters, null, false,
                                                                                          strSearchTitle, strSearchInstructionalText,
                                                                                          null, srlf);

                // Only continue if something was returned after clicking OK/Cancel/Close
                if (multiSelectResult != null)
                {
                    // If the User pressed the "OK" button...
                    if (multiSelectResult.SelectedButtonIdentifier == "1")
                    {
                        if (multiSelectResult.SelectedRecords != null)
                        {
                            if (multiSelectResult.SelectedRecords.Rows.Count >= 1)
                            {
                                // Even if the User has selected > 1 record for some odd reason (this is meant to be single-select), just grab
                                // the first selected record.
                                vntSelectedRecordId = Id.Create(multiSelectResult.SelectedRecords.Rows[0][strFieldNameToReturn]);

                                if (vntSelectedRecordId != null)
                                {
                                    return vntSelectedRecordId;
                                }
                                else
                                {
                                    // Throw error if null Record Id returned from record - this should never happen
                                    throw new PivotalApplicationCoreException("A record was selected from the results of the '" + strQueryName + "' Query which, for some reason, has a null Record Id.  This should never happen.  Please contact your Administrator.");
                                }
                            }
                        }
                    }
                }

                // Return either the Selected Record Id, or Null (if the User pressed cancel, or selected > 1 record, etc).
                return vntSelectedRecordId;
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Executes the specified Query, and returns the selected Record Id from the Query Results.  If the User selects multiple rows for some reason, then the first selected record
        /// is returned.  The field name specified in the strFieldNameToReturn variable must be the Primary Key Record Id field of the Query's Table (the Table Name is also specified
        /// in the strTableName variable).  So, if for example, you are executing a Query against the Account Table, then strFieldNameToReturn must be "Account_Id".
        /// This overload allows you to supply Parameter Names and Values to the Query being executed.  The parameterNames[] and parameterValues[]
        /// arrays must have the same numbers of elements.
        /// </summary>
        /// <param name="strQueryName">Name of Query to execute</param>
        /// <param name="strTableName">Name of the Table which the Query belongs to</param>
        /// <param name="strSearchTitle">The text to put in the Title section of the Query window.  Supply empty string for no text.</param>
        /// <param name="strSearchInstructionalText">Helper text, indicating what the User should do.  Supply empty string for no text.</param>
        /// <param name="strFieldNameToReturn">Primary Key Field Name of the Query's Table</param>
        /// <param name="parameterNames">String array of the Names of the Parameters to pass to the Query prior to its execution.  Must have the same number of elements as parameterValues.</param>
        /// <param name="parameterValues">Object array of the Values of the Parameters to pass to the Query prior to its execution.  Must have the same number of elements as parameterNames.</param>
        /// <param name="strSearchResultsListName">Name of the Search Results List within which the results should be displayed.</param>
        /// <returns></returns>
        public static Id SelectSingleRecordFromQuery(string strQueryName, string strTableName, string strSearchTitle, string strSearchInstructionalText, string strFieldNameToReturn,
                                                     string[] parameterNames, object[] parameterValues, string strSearchResultsListName)
        {
            try
            {
                // The selected Record Id (if any) will be returned in this variable.
                Id vntSelectedRecordId = null;

                // Get the Search Service, so we can execute Searches/Queries
                ISearchService searchService = ClientContext.ClientAPIService.GetService<ISearchService>();

                // Important: We need this searchParameters object, even if we don't do anything with it (other than supply it to the ShowMultiSelectModal call), as if
                // we don't supply it to ShowMultiSelectModal, then the "Run Search" button will disappear after the first search is executed.
                SearchParamsCollection searchParameters = new SearchParamsCollection();

                // At least one of the parameterNames or parameterValues is non-null, so we likely have to pass parameters to the Query
                if ((parameterNames != null) || (parameterValues != null))
                {
                    // parameterNames is null though - both should be non-null.  Throw error.
                    if (parameterNames == null)
                    {
                        throw new PivotalApplicationCoreException("A non-null parameterValues array was supplied, but the parameterNames array is null.  Both arrays must either be null, or non-null and contain the same number of elements.");
                    }

                    // parameterValues is null though - both should be non-null.  Throw error.
                    if (parameterValues == null)
                    {
                        throw new PivotalApplicationCoreException("A non-null parameterNames array was supplied, but the parameterValues array is null.  Both arrays must either be null, or non-null and contain the same number of elements.");
                    }

                    // Both parameter arrays are non-null but do not have the same length.  Throw error.
                    if (parameterNames.Length != parameterValues.Length)
                    {
                        throw new PivotalApplicationCoreException("The parameterNames and parameterValues arrays do not contain the same numbers of elements.  Unable to execute Query.");
                    }

                    // Iterate through Parameter Name and Value arrays, adding each pair as a Parameter to the Search Parameters collection
                    if (parameterNames.Length > 0)
                    {
                        for (int i = 0; i < parameterNames.Length; i++)
                        {
                            SearchParam sp = new SearchParam();
                            sp.ParamName = parameterNames[i];
                            sp.ParamValue = parameterValues[i];
                            searchParameters.Add(sp);
                        }
                    }
                }

                // Get the supplied Search Results List name
                SystemSearchResultsList ssrl = SystemClient.UserProfile.GetMetaItem<SystemSearchResultsList>(strSearchResultsListName);
                ISearchResultsListFields srlf = searchService.CreateSearchResultsListFields();
                srlf.SRLId = ssrl.Id;

                // Execute the supplied Query
                IMultiSelectResult multiSelectResult = searchService.ShowMultiSelectModal(strQueryName, strTableName,
                                                                                          searchParameters, null, false,
                                                                                          strSearchTitle, strSearchInstructionalText,
                                                                                          null, srlf);

                // Only continue if something was returned after clicking OK/Cancel/Close
                if (multiSelectResult != null)
                {
                    // If the User pressed the "OK" button...
                    if (multiSelectResult.SelectedButtonIdentifier == "1")
                    {
                        if (multiSelectResult.SelectedRecords != null)
                        {
                            if (multiSelectResult.SelectedRecords.Rows.Count >= 1)
                            {
                                // Even if the User has selected > 1 record for some odd reason (this is meant to be single-select), just grab
                                // the first selected record.
                                vntSelectedRecordId = Id.Create(multiSelectResult.SelectedRecords.Rows[0][strFieldNameToReturn]);

                                if (vntSelectedRecordId != null)
                                {
                                    return vntSelectedRecordId;
                                }
                                else
                                {
                                    // Throw error if null Record Id returned from record - this should never happen
                                    throw new PivotalApplicationCoreException("A record was selected from the results of the '" + strQueryName + "' Query which, for some reason, has a null Record Id.  This should never happen.  Please contact your Administrator.");
                                }
                            }
                        }
                    }
                }

                // Return either the Selected Record Id, or Null (if the User pressed cancel, or selected > 1 record, etc).
                return vntSelectedRecordId;
            }
            catch (Exception exc)
            {
                throw new PivotalApplicationCoreException(exc.Message, exc);
            }
        }

        /// <summary>
        /// Replacement for OTB PAFL Globals.SqlFind method, which really doesn't work very well.
        /// Foundation.Client.Globals.SQLIndex will try to check
        /// whether the default SRL is available, and if so, whether the column requested is in the SRL. If so,
        /// the routine will attempt to execute SQLIndex purely on the client side.  HOWEVER, if the field is 
        /// "Select From Personalization", then the field IS in the meta, so the client side approach will be
        /// used, but if the current user has not added that field to their column list, the attempt to access
        /// the column value will throw an exception.
        /// </summary>
        /// <param name="tableName">Table name to search within</param>
        /// <param name="filterColumnName">Table field to search for the supplied data within</param>
        /// <param name="filterColumnValue">Supplied value.  Look for this value in filterColumnName, and if found, return the Id of the first such record.</param>
        /// <returns>The Id value from the database.</returns>
        public static Id ServerSqlFind(string tableName, string filterColumnName, object filterColumnValue)
        {
            try
            {
                // Call the OTB **Server-Side** SqlFind method to return the data we need.
                return Globals.SystemClient.ExecuteServerTask("Core Data Access",
                                                              "SqlFind",
                                                              new Type[] { typeof(string), typeof(string), typeof(object) }, new object[] { tableName, filterColumnName, filterColumnValue }) as Id;
            }
            catch (Exception e)
            {
                throw new PivotalApplicationCoreException(e.Message, e);
            }
        }

        /// <summary>
        /// Replacement for OTB PAFL Globals.SqlFind method, which really doesn't work very well.
        /// Foundation.Client.Globals.SQLIndex will try to check
        /// whether the default SRL is available, and if so, whether the column requested is in the SRL. If so,
        /// the routine will attempt to execute SQLIndex purely on the client side.  HOWEVER, if the field is 
        /// "Select From Personalization", then the field IS in the meta, so the client side approach will be
        /// used, but if the current user has not added that field to their column list, the attempt to access
        /// the column value will throw an exception.
        /// </summary>
        /// <param name="tableName">Table name to search within</param>
        /// <param name="filterColumnName">Table field to search for the supplied data within</param>
        /// <param name="filterColumnValue">Supplied value.  Look for this value in filterColumnName, and if found, return the Id of the first such record.</param>
        /// <param name="findColumnName">Field from the found-record from which data should be returned.</param>
        /// <returns>The field value from the database.</returns>
        public static object ServerSqlFind(string tableName, string filterColumnName, object filterColumnValue, string findColumnName)
        {
            try
            {
                // Call the OTB **Server-Side** SqlFind method to return the data we need.
                return Globals.SystemClient.ExecuteServerTask("Core Data Access",
                                                              "SqlFind",
                                                              new Type[] { typeof(string), typeof(string), typeof(object), typeof(string) }, new object[] { tableName, filterColumnName, filterColumnValue, findColumnName });
            }
            catch (Exception e)
            {
                throw new PivotalApplicationCoreException(e.Message, e);
            }
        }

        /// <summary>
        /// Replacement for OTB PAFL Globals.SqlFind method, which really doesn't work very well.
        /// Foundation.Client.Globals.SQLIndex will try to check
        /// whether the default SRL is available, and if so, whether the column requested is in the SRL. If so,
        /// the routine will attempt to execute SQLIndex purely on the client side.  HOWEVER, if the field is 
        /// "Select From Personalization", then the field IS in the meta, so the client side approach will be
        /// used, but if the current user has not added that field to their column list, the attempt to access
        /// the column value will throw an exception.
        /// </summary>
        /// <param name="tableName">Table name to search within</param>
        /// <param name="columnName">Field name from which data should be returned, assuming that a record with id matching recordId is found.</param>
        /// <param name="recordId">Search for the record in tableName table with this record id, and if found, return the data in columnName.</param>        
        /// <returns>The field value from the database.</returns>
        public static object ServerSqlIndex(string tableName, string columnName, Id recordId)
        {
            try
            {
                // Call the OTB **Server-Side** SqlIndex method to return the data we need.
                return Globals.SystemClient.ExecuteServerTask("Core Data Access",
                                                              "SqlIndex",
                                                              new Type[] { typeof(string), typeof(string), typeof(Id) }, new object[] { tableName, columnName, recordId });
            }
            catch (Exception e)
            {
                throw new PivotalApplicationCoreException(e.Message, e);
            }
        }

        /// <summary>
        /// Set or clear the Required flag on fields on the current form. If the requested fields 
        /// does not have a required property no exception will be raised.
        /// </summary>
        /// <param name="Required">Whether the field should be required or not</param>
        /// <param name="DataFieldNames">String array of the DataBase names of the fields</param>
        public static void SetFieldRequired(bool Required, string[] DataFieldNames)
        {
            try
            {
                IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                foreach (string fn in DataFieldNames)
                {
                    // Not all Pivotal controls have a "Required" property.
                    // Therefore, we need to use System.Reflection to do the .NET equivialent
                    // of late binding in order to access the property and set it without
                    // throwing an exception and catching it again, which is very expensive.
                    PivotalControl ControlObject = formControl.GetControlByDataName(fn);
                    System.Reflection.PropertyInfo propInfo = ControlObject.GetType().GetProperty("Required");

                    if (propInfo != null)
                    {
                        propInfo.SetValue(ControlObject, Required, null);
                    }
                }
            }
            catch (Exception exc)
            {
                Globals.HandleException(exc, true);
            }
        }

        /// <summary>
        /// Set or clear the Required flag on a single field on the current form. If the requested field 
        /// does not have a required property no exception will be raised.
        /// </summary>
        /// <param name="Required">Whether the field should be required or not</param>
        /// <param name="DataFieldName">The database name of the field requested</param>
        public static void SetFieldRequired(bool Required, string DataFieldName)
        {
            SetFieldRequired(Required, new string[] { DataFieldName });
        }

        /// <summary>
        /// Specify a FK filter query by name, and set filter parameter map
        /// </summary>
        /// <param name="DataFieldName">The DataFieldName of the FK control to be set. This will be used in a call to GetControlByDataName</param>
        /// <param name="FilterName">The name of the query to be set as the FK filter.</param>
        /// <param name="ParameterMap">An array of strings specifying the form fields to be provided as the parameter map.</param>
        public static void SetFKFilter(string DataFieldName, string FilterName, string[] ParameterMap)
        {
            if (!string.IsNullOrEmpty(DataFieldName) && !string.IsNullOrEmpty(FilterName))
            {
                try
                {
                    IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                    PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                    // Instead of the usual cast like (PivotalForeignKey), using "as PivotalForeignKey" will
                    // never throw an "InvalidCast" exception, instead will set the object to null, saving 
                    // the try/catch that would otherwise be necessary
                    PivotalForeignKey ForeignKeyControl = formControl.GetControlByDataName(DataFieldName) as PivotalForeignKey;

                    if (ForeignKeyControl != null)
                    {
                        if (FilterName == string.Empty)
                        {
                            ForeignKeyControl.ForeignKeyFilterId = null;
                        }
                        else
                        {
                            Id FilterQueryId = Globals.GetMetaItemId<SystemSearch>(FilterName);
                            ForeignKeyControl.ForeignKeyFilterId = FilterQueryId.ToByteArray();
                        }
                    }

                    // Remove the if != null, we need to be able to clear the params if they were previously set
                    ForeignKeyControl.FilterArgumentColumnNames = ParameterMap;

                    // This is not necessary, and possibly causes warnings at run-time
                    // ForeignKeyControl.FilterArguments = ParamVals;
                }
                catch (Exception exc)
                {
                    Globals.HandleException(exc, true);
                }
            }
        }

        /// <summary>
        /// Specify a FK filter query by name
        /// </summary>
        /// <param name="DataFieldName">The DataFieldName of the FK control to be set. This will be used in a call to GetControlByDataName</param>
        /// <param name="FilterName">The NAME of the query to be set as the FK filter.</param>
        public static void SetFKFilter(string DataFieldName, string FilterName)
        {
            SetFKFilter(DataFieldName, FilterName, null);
        }

        /// <summary>
        /// Used in button clicks and secondary add: If current form is
        /// dirty, prompt user to save, and do it.
        /// </summary>
        /// <returns>True if the form is still dirty or invalid (and the caller should not continue) by the time the method returns. Otherwise false.</returns>
        public static Boolean CheckandApply()
        {
            IFormView formView = ClientContext.ClientAPIService.GetActiveContentObject() as IFormView;
            if (formView != null)
            {
                if (formView.Dirty)
                {
                    string strMessage = Globals.SystemClient.UserProfile.GetMetaItem<ResourceBundle>("Common").GetString("Save Record");

                    DialogResult choice = PivotalMessageBox.Show(strMessage, MessageBoxButtons.OKCancel);

                    if (choice != DialogResult.OK)
                    {
                        return true;
                    }
                    // Verify data
                    if (formView.VerifyRecord(out strMessage) == false || formView.ValidateRequiredFields(out strMessage) == false)
                    {
                        PivotalMessageBox.Show(strMessage, MessageBoxButtons.OK);
                        return true;
                    }
                    try
                    {
                        formView.SaveRecord();
                    }
                    catch (Exception exc)
                    {
                        PivotalMessageBox.Show(exc.Message, MessageBoxButtons.OK);
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Retrieves the table permissions for the specified form.
        /// </summary>
        /// <param name="formName">Client form name</param>
        /// <returns>A CdcSoftware.Pivotal.Engine.Types.Security.TableSettings object</returns>
        public static CdcSoftware.Pivotal.Engine.Types.Security.TableSettings CheckTablePermissions(string formName)
        {
            Id tableId = Globals.GetMetaItemTableId<CdcSoftware.Pivotal.Engine.Client.Types.Form>("TLA Contact");

            if (tableId == null)
            {
                throw new PivotalApplicationCoreException("Call to CheckTablePermissions() with formName = '" + formName + "' failed. The table for that form cannot be found.");
            }

            TableSettings settings = Globals.SystemClient.UserProfile.GetMetaSettings<TableSettings>(Globals.SystemClient.UserProfile.GetMetaItem<CdcSoftware.Pivotal.Engine.Types.Database.Table>(tableId));

            return settings;
        }

        /// <summary>
        /// Safely return any previous "original" version of a column, as it is in the database, without raising exceptions.
        /// </summary>
        /// <param name="row">A DataRow</param>
        /// <param name="columnName">The name of the column in the row.</param>
        /// <returns>The value of the column as it is currently in the database.</returns>
        public static object GetColumnOriginalVersion(DataRow row, string columnName)
        {
            if (row.HasVersion(DataRowVersion.Original))
            {
                return row[columnName, DataRowVersion.Original];
            }
            return DBNull.Value;
        }

        /// <summary>
        /// Gets the report Id from the name of the report.
        /// </summary>
        /// <param name="reportName">The name of the report.</param>
        /// <returns>The Id of the report.</returns>
        private static Id GetReportId(string reportName)
        {
            Id reportId = Globals.GetMetaItemId<SystemReport>(reportName);

            if (reportId == null)
            {
                return Globals.GetMetaItemId<UserReport>(reportName);
            }
            else
            {
                return reportId;
            }
        }

        /// <summary>
        /// Gets the table Id of a report from the name of the report.
        /// </summary>
        /// <param name="reportName">The name of the report.</param>
        /// <returns>The table Id of the report.</returns>
        private static Id GetReportTableId(string reportName)
        {
            Id tableId = Globals.GetMetaItemTableId<SystemReport>(reportName);

            if (tableId == null)
            {
                return Globals.GetMetaItemTableId<UserReport>(reportName);
            }
            else
            {
                return tableId;
            }
        }

        /// <summary>
        /// Returns the segment name for the secondary sender of an event.
        /// </summary>
        /// <param name="sender">The secondary control that generated the click event</param>
        /// <returns>Segment name</returns>
        public static string GetSenderSecondarySegmentName(PivotalControl sender)
        {
            // Get the DataTable for the secondary in play.
            PivotalSecondary pivotalSecondary = sender as PivotalSecondary;
            string segmentName = pivotalSecondary.DataSegment;
            return segmentName;
        }

        /// <summary>
        /// Give focus to a particular tab on a tab control.
        /// </summary>
        /// <param name="tabControlName">Name of tab control.</param>
        /// <param name="tabName">Name of tab within the tab control.</param>
        public static void GiveTabFocus(string tabControlName, string tabName)
        {
            try
            {
                IFormView formView = (IFormView)ClientContext.ClientAPIService.GetActiveContentObject();
                PivotalClientFormControl formControl = (PivotalClientFormControl)formView.FormControl;

                PivotalTabContainer pivotalTabContainer = formControl.GetControlByName(tabControlName) as PivotalTabContainer;
                PivotalTabPage pivotalTabPage = (PivotalTabPage)formControl.GetControlByName(tabName);
                int tabPageIndex = pivotalTabContainer.TabPages.IndexOfKey(pivotalTabPage.Name);
                pivotalTabContainer.SelectedIndex = tabPageIndex;
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Launches the specified form for the secondary row that has been clicked.
        /// </summary>
        /// <param name="sender">The secondary control that generated the click event</param>
        /// <param name="args">The event args for the click event</param>
        /// <param name="thisDataSet">The form's DataSet. Usually this.DataSet</param>
        /// <param name="recordIdFieldName">The name of the primary key for this secondary. E.g. Contact_Id</param>
        /// <param name="formName">The form to display modallly.</param>
        public static void ShowModalFormForSecondaryEditClick(PivotalControl sender, EventArgs args, DataSet thisDataSet, string recordIdFieldName, string formName)
        {
            string segmentName = GetSenderSecondarySegmentName(sender);
            DataTable records = thisDataSet.Tables[segmentName];

            // Discover the secondary row that was clicked.
            DataGridViewCellCancelEventArgs dataGridViewCellCancelEventArgs = args as DataGridViewCellCancelEventArgs;
            int rowNum = dataGridViewCellCancelEventArgs.RowIndex;

            // Get the record id of the Alt_Address we want to show
            Id recordId = Id.Create(records.Rows[rowNum][recordIdFieldName]);

            // Show modal Alt_Address form
            Globals.ShowModalForm(formName, recordId, null);
        }

        /// <summary>
        /// Based on FL ShowModal() method.
        /// Open a new smart client popup form with the specified form name or table name, record Id and parameterlist.
        /// </summary>
        /// <param name="formOrTableName">The form name or table name that the form based on.</param>
        /// <param name="recordId">The record to be opened.</param>
        /// <param name="parameterList">The ParameterList to be passed to the Client Task and/or Server Task associated to
        /// the form.</param>
        /// <returns>The primary record id for the form.  If the return Id is null, it means the record is deleted.</returns>
        public static Id ShowPopupForm(string formOrTableName, Id recordId, ParameterList parameterList)
        {
            string tableName = GetTableName(formOrTableName);

            IActionService actionService = ClientContext.ClientAPIService.GetService<IActionService>();
            IFormActionTarget formActionTarget = actionService.CreateActionTarget<IFormActionTarget>();

            if (tableName.Length > 0)
            {
                formActionTarget.Form = formOrTableName;
                formActionTarget.Table = tableName;
            }
            else
            {
                formActionTarget.Table = formOrTableName;
            }
            formActionTarget.RecordId = recordId;
            formActionTarget.ParameterList = parameterList;
            IAction action = actionService.CreateAction(string.Empty, ActionCommand.Show, ActionContent.Record, formActionTarget);
            action.TargetWindow = ActionTargetWindow.Popup;
            actionService.ExecuteAction(action);
            object[] modalReturnValue = (object[])action.ModalReturnValue;
            Id returnRecordId = null;
            if (TypeConvert.ToInt32(modalReturnValue[0]) == 1)
            {
                returnRecordId = Id.Create(modalReturnValue[1]);
            }
            return returnRecordId;
        }

        /// <summary>
        /// Gets the table Id that the give a meta item based on.
        /// </summary>
        /// <typeparam name="TItem">The type of the meta item.</typeparam>
        /// <param name="itemName">The name of the meta item.</param>
        /// <returns>The Id of the meta item.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004")]
        public static Id GetMetaItemTableId<TItem>(string itemName) where TItem : IMetadataItem
        {
            try
            {
                TItem item = Globals.SystemClient.UserProfile.GetMetaItem<TItem>(itemName);
                switch (item.GetType().Name)
                {
                    case "CdcSoftware.Pivotal.Engine.Client.Types.Form":
                    case "Form":
                        DataTemplate dataTemplate = Globals.SystemClient.UserProfile.GetMetaItem<DataTemplate>((item as CdcSoftware.Pivotal.Engine
                            .Client.Types.Form).DataTemplateId);
                        return dataTemplate.PrimaryDataSection.Table.Id;
                    case "CdcSoftware.Pivotal.Engine.Types.Reports.SystemReport":
                    case "SystemReport":
                        return (item as SystemReport).Table.Id;
                    case "CdcSoftware.Pivotal.Engine.Types.Database.Table":
                    case "Table":
                        return item.Id;
                }
                return null;
            }
            catch (KeyNotFoundException)
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the id of a meta item from the name of the meta item.
        /// </summary>
        /// <typeparam name="TItem">The type of the meta item.</typeparam>
        /// <param name="itemName">The name of the meta item.</param>
        /// <returns>The Id of the meta item.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031")]
        public static Id GetMetaItemId<TItem>(string itemName) where TItem : IMetadataItem
        {
            try
            {
                TItem item = Globals.SystemClient.UserProfile.GetMetaItem<TItem>(itemName);
                return item.Id;
            }
            catch
            {
                try
                {
                    UserReport report = Globals.SystemClient.UserProfile.GetMetaItem<UserReport>(itemName);
                    return report.Id;
                }
                catch (KeyNotFoundException)
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// This function is a Global client script to show a modal dialog form
        /// </summary>
        /// <param name="message">The message to describe the function of the dialog form</param>
        /// <param name="title">The title of the dialog form</param>
        /// <param name="buttonTexts">A string array to hold the button text to be shown on the dialog form</param>
        /// <returns>
        /// The index start from 1 user selected indicate the order of the button
        /// If the user click the close button on the upper-right of the form, the returned
        /// will be -1.</returns>
        public static int PcmDialog(string message, string title, params string[] buttonTexts)
        {
            DialogParam dialogParam = new DialogParam();
            dialogParam.DialogType = ModalDialogTypeEnum.modalDialogSegments;
            dialogParam.AddSection(string.Empty, message);

            // Adds buttons
            for (int i = 0; i < buttonTexts.Length; ++i)
            {
                dialogParam.AddButton(buttonTexts[i]);
            }

            IInputDialogService inputDialogService = ClientContext.ClientAPIService.GetService<IInputDialogService>();
            string strReturn = inputDialogService.ShowDialog(dialogParam, title);

            for (int i = 0; i < buttonTexts.Length; ++i)
            {
                if (TypeConvert.ToString(strReturn) == TypeConvert.ToString(buttonTexts[i]))
                {
                    return i;
                }
            }
            return -1;
        }

        /// <summary>
        /// This function to show a modal msgbox that has the same UI as dialog form provided
        /// by UIMaster. However, it has almost same interface as normal Msgbox function
        /// supported by VBScript
        /// </summary>
        /// <param name="message">The string expression displayed as the message in the dialog box.</param>
        /// <param name="button">The enumerator of MessageBoxButtons to indicate which button to display.</param>
        /// <param name="title">String expression displayed in the title bar of the dialog box.</param>
        /// Now this function supports following selections:
        /// vbOKOnly '0 Display OK button only.
        /// vbOKCancel '1 Display OK and Cancel buttons.
        /// vbAbortRetryIgnore '2 Display Abort, Retry, and Ignore buttons.
        /// vbYesNoCancel '3 Display Yes, No, and Cancel buttons.
        /// vbYesNo '4 Display Yes and No buttons.
        /// vbRetryCancel '5 Display Retry and Cancel buttons.
        /// <returns>Enumerator of DialogResult to indicate which button the user clicked.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031")]
        public static DialogResult PcmDialog(string message, string title, MessageBoxButtons button)
        {
            string abortButton = "Abort";
            string cancelButton = "Cancel";
            string ignoreButton = "Ignore";
            string okButton = "Ok";
            string noButton = "No";
            string retryButton = "Retry";
            string yesButton = "Yes";

            DialogParam dialogParam = new DialogParam();
            //dialogParam.Description = message;
            dialogParam.DialogType = ModalDialogTypeEnum.modalDialogSegments; // modalDialogParamsAndDescription;
            dialogParam.AddSection(string.Empty, message);

            switch (button)
            {
                case MessageBoxButtons.OK:
                    // 0 Display OK button only.
                    dialogParam.AddButton(okButton);
                    break;
                case MessageBoxButtons.OKCancel:
                    // 1 Display OK and Cancel buttons.
                    dialogParam.AddButton(okButton);
                    dialogParam.AddButton(cancelButton);
                    break;
                case MessageBoxButtons.AbortRetryIgnore:
                    // 2 Display Abort, Retry, and Ignore buttons.
                    dialogParam.AddButton(abortButton);
                    dialogParam.AddButton(retryButton);
                    dialogParam.AddButton(ignoreButton);
                    break;
                case MessageBoxButtons.YesNoCancel:
                    // 3 Display Yes, No, and Cancel buttons.
                    dialogParam.AddButton(yesButton);
                    dialogParam.AddButton(noButton);
                    dialogParam.AddButton(cancelButton);
                    break;
                case MessageBoxButtons.YesNo:
                    // 4 Display Yes and No buttons.
                    dialogParam.AddButton(yesButton);
                    dialogParam.AddButton(noButton);
                    break;
                case MessageBoxButtons.RetryCancel:
                    // 5 Display Retry and Cancel buttons.
                    dialogParam.AddButton(retryButton);
                    dialogParam.AddButton(cancelButton);
                    break;
                default:
                    dialogParam.AddButton(okButton);
                    break;
            }

            IInputDialogService inputDialogService = ClientContext.ClientAPIService.GetService<IInputDialogService>();
            string returnText = inputDialogService.ShowDialog(dialogParam, title);
            //return MessageBox.Show(message, title, button);
            if (returnText == okButton)
            {
                return DialogResult.OK;
            }
            else if (returnText == cancelButton)
            {
                return DialogResult.Cancel;
            }
            else if (returnText == yesButton)
            {
                return DialogResult.Yes;
            }
            else if (returnText == noButton)
            {
                return DialogResult.No;
            }
            else if (returnText == abortButton)
            {
                return DialogResult.Abort;
            }
            else if (returnText == retryButton)
            {
                return DialogResult.Retry;
            }
            else if (returnText == ignoreButton)
            {
                return DialogResult.Ignore;
            }
            else
            {
                switch (button)
                {
                    case MessageBoxButtons.AbortRetryIgnore:
                        return DialogResult.Ignore;
                    case MessageBoxButtons.OK:
                        return DialogResult.Cancel;
                    case MessageBoxButtons.OKCancel:
                        return DialogResult.Cancel;
                    case MessageBoxButtons.RetryCancel:
                        return DialogResult.Cancel;
                    case MessageBoxButtons.YesNo:
                        return DialogResult.No;
                    case MessageBoxButtons.YesNoCancel:
                        return DialogResult.Cancel;
                    default:
                        return DialogResult.Cancel;
                }
            }
        }

        /// <summary>
        /// Sets Cashier StatusStrip item text
        /// </summary>
        /// <history>
        /// #Revision   Date            Author  Description
        /// 6.0.9.1     19 Sep 2011     DS      Initial Version
        /// </history>
        public static void SetCashierStatusStrip(StatusStripPane pane)
        {
            if (Application.ProductName == "Smart Client Container")
            {
                // Update the status strip Environment text to the logged-in cashier's staff-id.
                string strCustomText = "Cashier: ";

                object posId = Globals.ServerSqlIndex("Employee", "RE_Store_POS_Id", SystemClient.UserProfile.EmployeeId);

                DataTable cashierLogin = Globals.GetDataTable("RE_All Cashier Logins for Today with POS?", new object[] { posId },
                    new string[] { "Staff_Code", "End_Time" });

                DataRow[] loggedInCashier = cashierLogin.AsEnumerable().Where(a => Convert.IsDBNull(a["End_Time"])).ToArray();
                if (loggedInCashier.Count() == 1)
                    strCustomText = strCustomText + loggedInCashier[0]["Staff_Code"].ToString();
                else
                    strCustomText = strCustomText + "---";
                if (pane == StatusStripPane.UserPane || pane == StatusStripPane.EnvironmentPane)
                    SetStatusBarItem(pane, strCustomText);
            }
        }

        /// <summary>
        /// Sets Statusbar item text
        /// </summary>
        /// <param name="StatusBarName"></param>
        /// <param name="StatusBarText"></param>
        /// <history>
        /// #Revision   Date            Author  Description
        /// 6.0.9.1     19 Sep 2011     DS      Initial Version
        /// </history>
        private static void SetStatusBarItem(StatusStripPane StatusBarName, string StatusBarText)
        {
            Hashtable barItems = new Hashtable();
            System.Windows.Controls.StackPanel stackPanel = (System.Windows.Controls.StackPanel)System.Windows.Application.Current.MainWindow.FindName("StatusBarZone");
            PopulateStatusBarItem(stackPanel, barItems);
            System.Windows.Controls.Primitives.StatusBarItem userPane = (System.Windows.Controls.Primitives.StatusBarItem)barItems[StatusBarName.ToString()];
            if (userPane != null) userPane.Content = "Environment: " + SystemClient.SystemInformation.SystemName + "  |  " + StatusBarText + "  ";
        }

        /// <summary>
        /// Populates barItems hashtable with statusbar items
        /// </summary>
        /// <param name="visual"></param>
        /// <param name="barItems"></param>
        /// <history>
        /// #Revision   Date            Author  Description
        /// 6.0.9.1     19 Sep 2011     DS      Initial Version
        /// </history>
        private static void PopulateStatusBarItem(Visual visual, Hashtable barItems)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
            {
                Visual childVisual = (Visual)VisualTreeHelper.GetChild(visual, i);
                if (childVisual.GetType().Name == "StatusBarItem")
                {
                    if (!string.IsNullOrEmpty(((System.Windows.Controls.Primitives.StatusBarItem)childVisual).Name))
                        barItems.Add(((System.Windows.Controls.Primitives.StatusBarItem)childVisual).Name, (System.Windows.Controls.Primitives.StatusBarItem)childVisual);
                }
                PopulateStatusBarItem(childVisual, barItems);
            }
        }

        /// <summary>
        /// Rounds off the Price with the Line of business's rounding logic.
        /// </summary>
        /// <param name="Price">Price to Round off.</param>
        /// <param name="LineOfBusinessId">Line of Business for the Employee.</param>
        /// <returns>Rounded Price</returns>
        public static double RoundOffPrice(double Price, object LineOfBusinessId)
        {
            //NN 08/08/2012:Issue 161
            //Commenting this part and applying the Split logic
            //double DecimalPart = Price - Math.Truncate(Price);
            // string strDecimalPart = DecimalPart.ToString();

            string strDecimalPart = string.Empty;
            double DecimalPart = 0.0;
            Price = Math.Round(Price, 2);
            string number = Convert.ToString(Price);
            string[] parts = number.Split(".".ToCharArray());
            if (parts != null && parts.Length == 2)
            {
                strDecimalPart = "0." + parts[1];
            }            
            
            string RoundingLogic = TypeConvert.ToString(Globals.ServerSqlIndex("RE_Line_Of_Business", "Rounding_Logic", Id.Create(LineOfBusinessId)));

            if (RoundingLogic.Contains("2 Decimal (nearest cent)"))
                return Price;
            else if (RoundingLogic.Contains("2 Decimal (5) (5 cents)"))
            {
                if (strDecimalPart.Length == 4)
                {
                    string lastBit = strDecimalPart.Substring(3, 1);
                    if (TypeConvert.ToInt32(lastBit) == 0 || TypeConvert.ToInt32(lastBit) == 5)
                        return Price;
                    else
                    {
                        if (TypeConvert.ToInt32(lastBit) < 5)
                        {
                            DecimalPart = (TypeConvert.ToDouble(lastBit) / 100);
                            Price = Price - DecimalPart;
                        }
                        else
                        {
                            DecimalPart = ((TypeConvert.ToDouble(lastBit) - 5) / 100);
                            Price = Price - DecimalPart;
                        }
                        return Price;
                    }
                }
                else
                    return Price;
            }
            else if (RoundingLogic.Contains("1 Decimal (10 cents)"))
            {
                if (strDecimalPart.Length == 4)
                {
                    string lastBit = strDecimalPart.Substring(3, 1);
                    if (TypeConvert.ToInt32(lastBit) == 0)
                        return Price;
                    else
                    {
                        DecimalPart = (TypeConvert.ToDouble(lastBit) / 100);
                        Price = Price - DecimalPart;
                        return Price;
                    }
                }
                else
                    return Price;
            }
            else
                return Price;
        }

        /// <summary>
        /// Checks if the current employee's store POS is Master POS or not.
        /// </summary>
        /// <returns>true, if Master POS else false.</returns>
        public static bool IsMasterPOS 
        {
            get { return TypeConvert.ToBoolean(Globals.ServerSqlIndex("RE_Store_POS", "Master_POS", 
                Id.Create(SqlIndex("Employee", "RE_Store_POS_Id", SystemClient.UserProfile.EmployeeId)))); }
        }

        #region Receipt Printing      

        /// <summary>
        ///  Connect to printer
        /// </summary>
        /// <param name="printer"></param>       
        public static void ConnectToPrinter(PosPrinter printer)
        {
            try
            {
                printer.Open();
                printer.Claim(10000);
                printer.DeviceEnabled = true;
                PrintError = false;
            }
            catch (PosControlException)
            {
                MessageBox.Show("POS Printer is not configured properly. Please check the printer.", "Pivotal Client", MessageBoxButtons.OK, MessageBoxIcon.Error);
                PrintError = true;
            }
            catch (PosException)
            {
                MessageBox.Show("POS Printer is not configured properly. Please check the printer.", "Pivotal Client", MessageBoxButtons.OK, MessageBoxIcon.Error);
                PrintError = true;
            }
            catch (Exception ex)
            {
                PrintError = true;
                throw ex;
            }
        }

        /// <summary>
        /// Disconnect From printer
        /// </summary>
        /// <param name="printer"></param>
        public static void DisconnectFromPrinter(PosPrinter printer)
        {
            try
            {
                printer.Release();
                printer.Close();
                // Delete the tempfile
                if (File.Exists(tempFile))
                    File.Delete(tempFile); 
            }
            catch (IOException)
            {
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Print the Text 
        /// </summary>
        /// <history>
        /// #Revision   Date            Author      Description
        /// 6.0.9.1     18 Apr 2012     starani     Initial Version
        /// </history>
        /// <param name="printer"></param>
        /// <param name="text"></param>
        private static void PrintText(PosPrinter printer, string text)
        {
            printer.PrintNormal(PrinterStation.Receipt, text); 
        }

        /// <summary>
        /// Print the Line 
        /// </summary>
        /// <history>
        /// #Revision   Date            Author      Description
        /// 6.0.9.1     18 Apr 2012     starani     Initial Version
        /// </history>
        /// <param name="printer"></param>
        /// <param name="text"></param>
        public static void PrintTextLine(PosPrinter printer, string text)
        {
            printer.PrintNormal(PrinterStation.Receipt, text + Environment.NewLine); //Print text, then a new line character.
            //try
            //{
            //    if (text.Length <= printer.RecLineChars)
            //        printer.PrintNormal(PrinterStation.Receipt, text + Environment.NewLine); //Print text, then a new line character.
            //    else if (text.Length > printer.RecLineChars)
            //    {
            //        int index = 0;
            //        for (int i = 0; i < Convert.ToInt32(Math.Ceiling(Convert.ToDouble(text.Length) / Convert.ToDouble(printer.RecLineChars)),
            //            CultureInfo.InvariantCulture.NumberFormat); i++)
            //        {
            //            if ((i + 1) * printer.RecLineChars <= text.Length)
            //                printer.PrintNormal(PrinterStation.Receipt, text.Substring(index, printer.RecLineChars));
            //            else
            //                printer.PrintNormal(PrinterStation.Receipt, text.Substring(index, text.Length - index));
            //            //PrintTextLine(printer, Environment.NewLine);
            //            index = index + printer.RecLineChars;
            //        }
            //        //string str = text.Substring(0, printer.RecLineChars);
            //        //printer.PrintNormal(PrinterStation.Receipt, TruncateAt(text, printer.RecLineChars)); //Print exactly as many characters as the printer allows, truncating the rest, no new line character (printer will probably auto-feed for us)
            //    }
            //}
            //catch
            //{
            //    throw;
            //}
        }

        /// <summary>
        /// Print the Header of the Receipt
        /// </summary>
        /// <history>
        /// #Revision   Date            Author      Description
        /// 6.0.9.1     19 Apr 2012     starani     Initial Version
        /// </history>  
        /// <param name="printer"></param>
        /// <param name="StoreName"></param>
        /// <param name="addressLine1"></param>
        /// <param name="storePhone"></param>
        /// <param name="taxRefNumber"></param>
        /// <param name="custService"></param>
        /// <param name="mailId"></param>
        /// <param name="dateTime"></param>
        /// <param name="cashierName"></param>
        /// <param name="receiptNo"></param>
        /// <param name="storeLogo"></param>        
        public static void PrintReceiptHeader(PosPrinter printer, string StoreName, string addressLine1, string storePhone, string taxRefNumber, string custService,
            string mailId, DateTime dateTime, string cashierName, string receiptNo, object storeLogo, bool gift)
        {
            try
            {
                PrintImageFromDB(printer, storeLogo);
                PrintTextLine(printer, Environment.NewLine);
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + StoreName);
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + addressLine1);
                if (!Convert.IsDBNull(storePhone) && (storePhone != ""))
                    PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + "PHONE No: " + storePhone);
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + "GST REGISTRATION: " + taxRefNumber);
                if (!Convert.IsDBNull(custService) && (custService !=""))
                    PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + "CUSTOMER SERVICE NUMBER: " + custService);
                if (!Convert.IsDBNull(mailId) && (mailId != ""))
                    PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + mailId);
                PrintTextLine(printer, new string('_', printer.RecLineChars));
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + "TAX INVOICE NUMBER: " + receiptNo);
                PrintText(printer, dateTime.Date.ToString("dd/MM/yyyy"));
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'r', (byte)'A' }) + String.Format("{0}", dateTime.ToLongTimeString()));
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + "CASHIER: " + cashierName);
                PrintTextLine(printer, new string('_', printer.RecLineChars));
                PrintText(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + "ART".PadRight(18) + ' ');
                PrintText(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + "DESC".PadRight(printer.RecLineChars - (18 + 1 + 3 + 1 + 8 + 1)) + ' ');
                PrintText(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + "QTY".PadLeft(3) + ' ');
                if (gift)
                    PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + string.Empty.PadLeft(8));
                else
                {
                    PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + "AMT".PadLeft(8));
                    PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + "(SGD)".PadLeft(printer.RecLineChars));
                }
            }
            catch
            {
                throw;
            }
        }
        
        /// <summary>
        /// Print the line items of products
        /// </summary>
        /// <param name="printer">POS printer object</param>
        /// <param name="articleNumber">Article Number of the line item.</param>
        /// <param name="colorDescription">Color Description for the line item.</param>
        /// <param name="quantity">Quantity for the line item.</param>
        /// <param name="extenderPrice">Extended Price for the line item.</param>
        /// <param name="tier1DiscountId">Tier 1 Discount Id for the line item.</param>
        /// <param name="tier1DiscountValueType">Tier 1 Value type like "Percent" for the line item.</param>
        /// <param name="tier1DiscountPercentValue">If percent, then the percent value for the line item.</param>
        /// <param name="tier1DiscountAmount">If not percent, then the discount amount for the line item.</param>
        /// <param name="tier1DiscountValue">Total Discount for the line item.</param>
        /// <param name="discCondition1Description">Discount description for Tier 1 discount.</param>
        /// <param name="tier2DiscountId">Tier 2 Discount Id for the line item.</param>
        /// <param name="tier2DiscountValueType">Tier 2 Value type like "Percent" for the line item.</param>
        /// <param name="tier2DiscountPercentValue">If percent, then the percent value for the line item.</param>
        /// <param name="tier2DiscountAmount">If not percent, then the discount amount for the line item.</param>
        /// <param name="tier2DiscountValue">Total Discount for the line item.</param>
        /// <param name="discCondition2Description">Discount description for Tier 2 discount.</param>
        /// <param name="itemDesciption">Item description for the line item.</param>
        /// <param name="sizeDescription">Size description for the line item.</param>
        /// <param name="gift">Specifies whether it is a Gift report or not.</param>
        public static void PrintLineItem(PosPrinter printer, string articleNumber, string colorDescription, int quantity, Double extenderPrice, object tier1DiscountId,
            string tier1DiscountValueType, double tier1DiscountPercentValue, double tier1DiscountAmount, double tier1DiscountValue, string discCondition1Description,
            object tier2DiscountId, string tier2DiscountValueType, double tier2DiscountPercentValue, double tier2DiscountAmount, double tier2DiscountValue, 
            string discCondition2Description, string itemDesciption, string sizeDescription, bool gift,string staffCode)
        {
            try
            {
                string qty = TypeConvert.ToString(quantity);
                string amt = String.Format(extenderPrice.ToString("#0.00"));
                int itemDescLen = (printer.RecLineChars - (18 + 1 + 1 + 3 + 1 + 8));

                //First Line for each line item
                PrintText(printer, articleNumber.PadRight(18));
                PrintText(printer, ' ' + (colorDescription.Substring(0, (colorDescription.Length > itemDescLen) ? itemDescLen : colorDescription.Length) + ' ').PadRight(itemDescLen));
                PrintText(printer, ' ' + qty.PadLeft(3));
                if (gift)
                    PrintTextLine(printer, ' ' + string.Empty.PadLeft(8));
                else
                    PrintTextLine(printer, ' ' + amt.PadLeft(8));

                //If color description exceeds allocated space, then flow it to new line.
                if (colorDescription.Length > itemDescLen)
                {
                    returnString = SplitStringToArray(colorDescription.Substring(itemDescLen, colorDescription.Length - itemDescLen), itemDescLen);
                    if (returnString.Length > 0)
                    {
                        foreach (string item in returnString)
                        {
                            PrintTextLine(printer, item.PadRight(itemDescLen).PadLeft(19 + itemDescLen));
                        }
                    }
                }
                //Change Request Reference Number - Q06112012CK - CR VIII. Added Staff Code in the reciept.
                PrintText(printer, staffCode.PadRight(18));

                //Item description + Size for each line item.
                string itemDescription = string.Empty;
                //if (!Convert.IsDBNull(itemDesciption) && TypeConvert.ToString(itemDesciption) != string.Empty)
                //{
                //    itemDescription = itemDesciption;
                //    if (!Convert.IsDBNull(sizeDescription) && TypeConvert.ToString(sizeDescription) != string.Empty)
                //        itemDescription = itemDescription + ' ' + "(Size " + sizeDescription + ')';
                //}
                //else
                //{
                //Change Request Reference Number - Q06112012CK. Commented the above If() Condition.
                if (!Convert.IsDBNull(sizeDescription) && TypeConvert.ToString(sizeDescription) != string.Empty)
                    itemDescription = itemDescription + " (" + sizeDescription + ')';
                //}

                //itemDescription = itemDesciption + ' ' + "(Size " + sizeDescription + ')';
                if (itemDescription != string.Empty)
                {
                    returnString = SplitStringToArray(itemDescription, itemDescLen);
                    foreach (string item in returnString)
                    {
                        //DS:09012013 - As the staff code is inserted now, there is no use of padleft.
                        //PrintTextLine(printer, item.PadRight(itemDescLen).PadLeft(19 + itemDescLen));
                        PrintTextLine(printer, item.PadRight(itemDescLen));
                    }
                }

                if (!gift)
                {
                    string discountDesc = string.Empty;
                    if (!Convert.IsDBNull(tier1DiscountId))
                    {
                        discountDesc = discCondition1Description + (string.Equals(tier1DiscountValueType, "Percent") ? " " : " $ ") +
                            (string.Equals(tier1DiscountValueType, "Percent") ? tier1DiscountPercentValue.ToString() + " % " : tier1DiscountAmount.ToString());

                        //First Line for Discount.
                        PrintText(printer, discountDesc.Substring(0, (discountDesc.Length > itemDescLen) ? itemDescLen : discountDesc.Length).PadRight(itemDescLen).PadLeft(19 + itemDescLen));
                        //if (gift)
                        //    PrintTextLine(printer, string.Empty.PadLeft(8 + 1 + 3 + 1));
                        //else
                        PrintTextLine(printer, ('(' + TypeConvert.ToDouble(tier1DiscountValue).ToString("#0.00") + ')').PadLeft(8 + 1 + 3 + 1));

                        //Print the next Lines.
                        if (discountDesc.Length > itemDescLen)
                        {
                            returnString = SplitStringToArray(discountDesc.Substring(itemDescLen, discountDesc.Length - itemDescLen), itemDescLen);
                            if (returnString.Length > 0)
                            {
                                foreach (string item in returnString)
                                {
                                    PrintTextLine(printer, item.PadRight(itemDescLen).PadLeft(19 + itemDescLen));
                                }
                            }
                        }
                    }
                    if (!Convert.IsDBNull(tier2DiscountId))
                    {
                        discountDesc = discCondition2Description + (string.Equals(tier2DiscountValueType, "Percent") ? " " : " $ ") +
                            (string.Equals(tier2DiscountValueType, "Percent") ? tier2DiscountPercentValue.ToString() + " % " : tier2DiscountAmount.ToString());

                        //First Line for Discount.
                        PrintText(printer, discountDesc.Substring(0, (discountDesc.Length > itemDescLen) ? itemDescLen : discountDesc.Length).PadRight(itemDescLen).PadLeft(19 + itemDescLen));
                        //if (gift)
                        //    PrintTextLine(printer, string.Empty.PadLeft(8 + 1 + 3 + 1));
                        //else
                        PrintTextLine(printer, ('(' + TypeConvert.ToDouble(tier2DiscountValue).ToString("#0.00") + ')').PadLeft(8 + 1 + 3 + 1));

                        //Print the next Lines.
                        if (discountDesc.Length > itemDescLen)
                        {
                            returnString = SplitStringToArray(discountDesc.Substring(itemDescLen, discountDesc.Length - itemDescLen), itemDescLen);
                            if (returnString.Length > 0)
                            {
                                foreach (string item in returnString)
                                {
                                    PrintTextLine(printer, item.PadRight(itemDescLen).PadLeft(19 + itemDescLen));
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Split the String with the number of characters.
        /// </summary>
        /// <param name="stringToSplit">Input string to split</param>
        /// <param name="length">lengh for each split</param>
        /// <returns>String array of the splitted strings.</returns>
        private static string[] SplitStringToArray(string stringToSplit, int length)
        {
            System.Globalization.StringInfo str = new System.Globalization.StringInfo(stringToSplit);

            int lengthAbs = Math.Abs(length);

            if (str == null || str.LengthInTextElements == 0 || lengthAbs == 0 || str.LengthInTextElements <= lengthAbs)
                return new string[] { str.String };

            string[] array = new string[(str.LengthInTextElements % lengthAbs == 0 ? str.LengthInTextElements / lengthAbs: (str.LengthInTextElements / lengthAbs) + 1)];

            if (length > 0)
                for (int iStr = 0, iArray = 0; iStr < str.LengthInTextElements && iArray < array.Length; iStr += lengthAbs, iArray++)
                    array[iArray] = str.SubstringByTextElements(iStr, (str.LengthInTextElements - iStr < lengthAbs ? str.LengthInTextElements - iStr : lengthAbs));
            else // if (length < 0)
                for (int iStr = str.LengthInTextElements - 1, iArray = array.Length - 1; iStr >= 0 && iArray >= 0; iStr -= lengthAbs, iArray--)
                    array[iArray] = str.SubstringByTextElements((iStr - lengthAbs < 0 ? 0 : iStr - lengthAbs + 1), (iStr - lengthAbs < 0 ? iStr + 1 : lengthAbs));

            return array;
        }

        /// <summary>
        /// Print the footer.
        /// </summary>
        /// <param name="printer"></param>
        /// <param name="thankYouNote"></param>
        /// <param name="termsNConditions"></param>
        /// <param name="website"></param>
        /// <param name="receiptNumber"></param>
        /// <param name="promotionalImage"></param>
        /// <param name="exchange"></param>
        /// <param name="reprint"></param>
        /// <param name="gift"></param>
        public static void PrintReceiptFooter(PosPrinter printer, string thankYouNote, OrderedEnumerableRowCollection<DataRow> termsNConditions, string website, 
             string receiptNumber, object promotionalImage, bool exchange, bool reprint, bool gift)
        {
            try
            {
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + thankYouNote);
                PrintTextLine(printer, new string('_', printer.RecLineChars));
                PrintImageFromDB(printer, promotionalImage);
                PrintTextLine(printer, new string('_', printer.RecLineChars));

                if (gift)
                {
                    PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) +
                        System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'2', (byte)'h', (byte)'C' }) + "GIFT RECEIPT");
                    PrintTextLine(printer, new string('_', printer.RecLineChars));
                }
                else
                {
                    if (exchange)
                    {
                        PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) +
                            System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'2', (byte)'h', (byte)'C' }) + "EXCHANGE RECEIPT");
                        PrintTextLine(printer, new string('_', printer.RecLineChars));
                    }
                    if (reprint)
                    {
                        PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) +
                            System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'2', (byte)'h', (byte)'C' }) + "DUPLICATE COPY");
                        PrintTextLine(printer, new string('_', printer.RecLineChars));
                    }
                }
                
                //Print the terms and conditions
                if (termsNConditions != null)
                {
                    foreach (DataRow dr in termsNConditions)
                    {
                        returnString = SplitStringToArrayWithWrap(TypeConvert.ToString(dr["Terms_And_Conditions"]), printer.RecLineChars);
                        if (returnString.Length > 0)
                        {
                            foreach (string item in returnString)
                            {
                                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + item);
                            }
                        }
                    }
                }
                PrintTextLine(printer, new string('_', printer.RecLineChars));
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'c', (byte)'A' }) + website);
                PrintBarcode(printer, receiptNumber);
                PrintTextLine(printer, Environment.NewLine);
                PrintTextLine(printer, Environment.NewLine);
                PrintTextLine(printer, Environment.NewLine);
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Splits the string for the number of characters preserving the words.
        /// </summary>
        /// <param name="sentence">Sentence to split</param>
        /// <param name="maxLength">Max length per line</param>
        /// <returns>String array containing preserver words</returns>
        private static string[] SplitStringToArrayWithWrap(string sentence, int maxLength)
        {
            List<string> parts = new List<string>();
            string[] pieces = sentence.Split(' ');
            StringBuilder tempString = new StringBuilder("");

            foreach (var piece in pieces)
            {
                if (piece.Length + tempString.Length + 1 > maxLength)
                {
                    parts.Add(tempString.ToString());
                    tempString.Clear();
                }
                tempString.Append((tempString.Length == 0 ? "" : " ") + piece);
            }
            if (tempString.Length > 0)
                parts.Add(tempString.ToString());

            return parts.ToArray();
        }

        /// <summary>
        /// print the barcode
        /// </summary>
        /// <param name="printer"></param>
        /// <param name="TextToPrint"></param>
        /// <history>
        /// #Revision   Date            Author      Description
        /// 6.0.9.1     23 Apr 2012     starani     Initial Version
        /// </history>
        /// <param name="printer"></param>
        /// <param name="ReceiptBarCode"></param>
        private static void PrintBarcode(PosPrinter printer, string ReceiptBarCode)
        {
            printer.PrintBarCode(PrinterStation.Receipt, ReceiptBarCode, BarCodeSymbology.Code39, 100, 20, PosPrinter.PrinterBarCodeCenter, BarCodeTextPosition.Below);
        }
       
        /// <summary>
        /// Print the Birthday Image
        /// </summary>
        /// <param name="printer"></param>
        /// <history>
        /// #Revision   Date            Author      Description
        /// 6.0.9.1     23 Apr 2012     starani     Initial Version
        /// </history>
        /// <param name="printer"></param>
        /// <param name="image"></param>
        private static void PrintImageFromDB(PosPrinter printer, object image)
        {
            tempFile = Path.ChangeExtension(Path.GetTempPath() + Guid.NewGuid().ToString(), ".bmp"); // Create the temp file
            File.WriteAllBytes(tempFile, (byte[])image);
            printer.PrintBitmap(PrinterStation.Receipt, tempFile, PosPrinter.PrinterBitmapAsIs, PosPrinter.PrinterBitmapCenter);
            if (File.Exists(tempFile))
                File.Delete(tempFile);
        }

        /// <summary>
        /// Print the totals section.
        /// </summary>
        /// <param name="printer"></param>
        /// <param name="total"></param>
        /// <param name="tax"></param>
        /// <param name="itemCount"></param>
        /// <param name="taxPercent"></param>
        /// <param name="gift"></param>
        public static void PrintTotals(PosPrinter printer, double total, double tax, int itemCount, double taxPercent, bool gift)
        {
            try
            {
                //Total Area
                if (!gift)
                {
                    PrintText(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) +
                        "TOTAL ".PadRight(printer.RecLineChars - 8 - 1 - 18 - 1).PadLeft(printer.RecLineChars - 8 - 1) + ' ');
                    if (gift)
                        PrintTextLine(printer, string.Empty.PadLeft(8));
                    else
                        PrintTextLine(printer, total.ToString("#0.00").PadLeft(8));

                    //Tax Area
                    PrintText(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) +
                        ("INCLUSIVE " + taxPercent.ToString() + "% GST").PadRight(printer.RecLineChars - 8 - 1) + ' ');
                    if (gift)
                        PrintTextLine(printer, string.Empty.PadLeft(8));
                    else
                        PrintTextLine(printer, tax.ToString("#0.00").PadLeft(8));
                }
                //Item Count Area
                PrintText(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) +
                    "ITEM COUNT".PadRight(printer.RecLineChars - 8 - 1 - 3 - 1) + ' ');
                PrintTextLine(printer, itemCount.ToString().PadLeft(3));
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Print Tender
        /// </summary>
        /// <param name="printer"></param>
        /// <history>
        /// #Revision   Date            Author      Description
        /// 6.0.9.1     23 Apr 2012     starani     Initial Version
        /// </history>
        /// <param name="TenderAmount"></param>
        /// <param name="TenderType"></param>
        public static void PrintTender(PosPrinter printer, DataTable tender)
        {
            try
            {
                PrintText(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + 
                    "PAYMENT MODE".PadRight(printer.RecLineChars));
                foreach (DataRow dr in tender.Rows)
                {
                    string tenderType = TypeConvert.ToString(dr["Tender_Type_Id__Tender_Type"]);
                    if (!string.Equals(tenderType.ToUpper(), "CHECK"))
                    {
                        int itemDescLen = (printer.RecLineChars - (18 + 1 + 1 + 8));
                        //Print the First Line.
                        PrintText(printer, tenderType.Substring(0, (tenderType.Length > itemDescLen) ? itemDescLen : tenderType.Length).PadRight(printer.RecLineChars - 8 - 1 - 18 - 1).PadLeft(printer.RecLineChars - 8 - 1) + ' ');
                        PrintTextLine(printer, TypeConvert.ToDouble(dr["Tendered_Amount"]).ToString("#0.00").PadLeft(8));

                        //Print the next Lines.
                        if (tenderType.Length > itemDescLen)
                        {
                            returnString = SplitStringToArray(tenderType.Substring(itemDescLen, tenderType.Length - itemDescLen), itemDescLen);
                            if (returnString.Length > 0)
                            {
                                foreach (string item in returnString)
                                {
                                    PrintTextLine(printer, item.PadRight(itemDescLen).PadLeft(19 + itemDescLen));
                                }
                            }
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Print Membership Details for the Member.
        /// </summary>
        /// <param name="printer"></param>
        /// <param name="firstName">First Name</param>
        /// <param name="lastName">Last Name</param>
        /// <param name="membershipID">Membership ID</param>
        /// <param name="printBirthdayWish">If today is Member's Birthdy and it needs to be printed, then this value is true. If this is false, then pass null for birthdayImage parameter.</param>
        /// <param name="birthdayImage">Image to print for Members Birthday. If the previous param is false, pass null.</param>
        public static void PrintPrivilegeCardDetails(PosPrinter printer, string firstName, string lastName, string membershipID, bool printBirthdayWish, object birthdayImage)
        {
            try
            {
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + "PRIVILEGE CARD MEMBER");
                returnString = SplitStringToArray(firstName + ' ' + lastName + ", " + membershipID, printer.RecLineChars);
                if (returnString.Length > 0)
                {
                    foreach (string item in returnString)
                    {
                        PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + item);
                    }
                }
                if (printBirthdayWish && !Convert.IsDBNull(birthdayImage))
                {
                    //Print Line.
                    PrintTextLine(printer, new string('_', printer.RecLineChars));
                    //Print Birthday Image.
                    PrintImageFromDB(printer, birthdayImage);
                }
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// Print Staff details for the staff sale.
        /// </summary>
        /// <param name="printer"></param>
        /// <param name="fullName">Full Name</param>
        /// <param name="topManagement">Whether the staff is topManagement or not. If true, then the next param can be null.</param>
        /// <param name="quota">Remaining Quota. If the staff is top management, then pass null</param>
        public static void PrintStaffDetails(PosPrinter printer, string fullName, bool topManagement, int? quota)
        {
            try
            {
                PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + 
                    ((topManagement)?"TOP MANAGEMENT":"STAFF"));
                returnString = SplitStringToArray(fullName, printer.RecLineChars);
                if (returnString.Length > 0)
                {
                    foreach (string item in returnString)
                    {
                        PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + item);
                    }
                }
                if (!topManagement)
                    PrintTextLine(printer, System.Text.ASCIIEncoding.ASCII.GetString(new byte[] { 27, (byte)'|', (byte)'b', (byte)'C' }) + "REMAINING QUOTA: " + quota.ToString());
            }
            catch
            {
                throw;
            }
        }
        
        #endregion

        #region Cash Drawer Kick
        
        ///// <summary>
        ///// Creates the Cash Drawer instance, Open the Cash Drawer and then close the connection.
        ///// </summary>
        //public static void OpenCashDrawer()
        //{
        //    CashDrawer cashDrawer = null;
        //    try
        //    {
        //        if (!PrintError)
        //        {
        //            //Get the device.
        //            PosExplorer posExplorer = new PosExplorer();
        //            DeviceCollection devices = posExplorer.GetDevices(DeviceType.CashDrawer);
        //            DeviceInfo cashDrawerDevice = posExplorer.GetDevice("CashDrawer", "CashDrawer");// "EPSON TM-T88IV Receipt", May need to change this if you don't use a logicial name or use a different one.
        //            cashDrawer = (CashDrawer)posExplorer.CreateInstance(cashDrawerDevice);

        //            if (cashDrawer != null)
        //            {
        //                //Connect to the device
        //                cashDrawer.Open();
        //                cashDrawer.Claim(9000);
        //                cashDrawer.PowerNotify = PowerNotification.Enabled;
        //                cashDrawer.DeviceEnabled = true;

        //                //Open the Drawer.
        //                cashDrawer.OpenDrawer();
        //            }
        //        }
        //        else
        //            CashDrawerError = true;
        //    }
        //    catch (PosControlException)
        //    {
        //        PivotalMessageBox.Show("Cash Drawer is not configured properly. Please check the cash drawer.", MessageBoxButtons.OK, MessageBoxIcon.Error);
        //        CashDrawerError = true;
        //    }
        //    catch (PosException)
        //    {
        //        PivotalMessageBox.Show("Cash Drawer is not configured properly. Please check the cash drawer.", MessageBoxButtons.OK, MessageBoxIcon.Error);
        //        CashDrawerError = true;
        //    }
        //    catch (Exception ex)
        //    {
        //        CashDrawerError = true;
        //        throw ex;                
        //    }
        //    finally
        //    {
        //        if (!CashDrawerError)
        //        {
        //            cashDrawer.Release();
        //            cashDrawer.Close();
        //        }
        //    }
        //}
        #endregion

        #region Apple Care Authentication

        public static void AppleCareAuthenticate()
        {
            try
            {
                DateTime currentTime = DateTime.Now;
                DateTime activeSession = AppleCareSessionDateTime.AddMinutes(25) ;

                if (activeSession <= currentTime)
                {
                    m_appleCareUserSessionId = string.Empty;
                    m_appleCareUserOperationId = string.Empty;
                    DataRow drLOB = Globals.GetDataTable("RE_Logged In Employee LOB", new object[]{SystemClient.UserProfile.EmployeeId}, new string[] { "RE_RAE_WebService_URL",
                                                          "RE_RAE_User_Id","RE_RAE_Password","RE_RAE_ServiceAccountNo","RE_RAE_LanguageCode","RE_RAE_UserTimeZone"}).Rows[0];

                    Pcm.Retail.Client.AppleCare.GsxWSApacResellerService appleCareService = new AppleCare.GsxWSApacResellerService();
                    appleCareService.Url = TypeConvert.ToString(drLOB["RE_RAE_WebService_URL"]);

                    AppleCare.authenticateRequestType ar = new AppleCare.authenticateRequestType();
                    ar.userId = TypeConvert.ToString(drLOB["RE_RAE_User_Id"]);
                    ar.password = TypeConvert.ToString(drLOB["RE_RAE_Password"]);
                    ar.serviceAccountNo = TypeConvert.ToString(drLOB["RE_RAE_ServiceAccountNo"]);
                    ar.languageCode = TypeConvert.ToString(drLOB["RE_RAE_LanguageCode"]);
                    ar.userTimeZone = TypeConvert.ToString(drLOB["RE_RAE_UserTimeZone"]);

                    AppleCare.authenticateResponseType response = appleCareService.Authenticate(ar);

                    m_appleCareUserSessionId = response.userSessionId;
                    m_appleCareUserOperationId = response.operationId;

                    AppleCareSessionDateTime = currentTime;
                }
            }
            catch (Exception exc)
            {
                
            }            
        }

        #endregion

        #endregion
    }
}
